/* Design tokens. Everything below - including the caption showcase further
   down this file - reads these, so the whole app rethemes from here. */
:root {
    --bg:           #10131a;
    --panel:        #191c22;
    --panel-2:      #212530;
    --border:       #32353c;
    --border-soft:  #24272f;
    --text:         #e0e2ec;
    --muted:        #9aa1b0;
    --accent:       #f5c518;   /* the highlight colour of a burned-in caption */
    --accent-hover: #ffe08b;
    --accent-ink:   #10131a;   /* always dark text on the accent, never white */
    --ok:           #3ecf7e;
    --err:          #ffb4ab;
    --font:         "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
    --mono:         "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
    --radius:       8px;
    --radius-lg:    12px;
}
* { box-sizing: border-box; }
/* The browser hides [hidden] with `display: none` at specificity 0,0,1, and
   this stylesheet sets display on `button` and on several classes - all of
   which beat it. So every button carrying the attribute was on screen anyway:
   Save sat above an untouched transcript, and the trim's Reset never went
   away. One rule rather than a [hidden] clause per component, because the
   next person to write `hidden` should not have to know this. */
[hidden] { display: none !important; }
body {
    background: var(--bg); color: var(--text); font-family: var(--font);
    margin: 0; padding: 0; -webkit-font-smoothing: antialiased;
}
.wrap { width: 100%; max-width: 720px; margin: 0 auto; padding: 32px 20px; }
/* A landing page needs more room than a form does. Widening the container is
   the whole fix - no viewport units, so the scrollbar cannot push the page
   sideways. */
.wrap-wide { max-width: 1120px; padding-top: 0; }
h1 { font-size: 24px; font-weight: 600; letter-spacing: -.01em; margin: 0 0 4px; }
.sub { color: var(--muted); font-size: 13px; margin-bottom: 24px; }
a { color: var(--accent); }

nav {
    display: flex; align-items: center; justify-content: space-between; gap: 16px;
    padding: 14px 24px; border-bottom: 1px solid var(--border); background: var(--panel);
}
nav .brand {
    display: inline-flex; align-items: center; gap: 8px;
    font-weight: 600; font-size: 17px; color: var(--text); text-decoration: none;
}
nav .brand img { width: 28px; height: 28px; border-radius: 7px; }
nav .links { display: flex; align-items: center; flex-wrap: wrap; gap: 18px; }
nav .links a { color: var(--muted); text-decoration: none; font-size: 14px; }
nav .links a:hover { color: var(--text); }
/* The balance is the number that matters most in a prepaid product, so it
   reads as a pill rather than as one more link. */
nav .credits {
    font-family: var(--mono); font-size: 12px; color: var(--accent);
    border: 1px solid var(--accent); border-radius: 999px; padding: 3px 10px; white-space: nowrap;
}

.panel {
    background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius-lg);
    padding: 20px; margin-bottom: 20px;
}
.dropzone {
    border: 2px dashed var(--border); border-radius: var(--radius); padding: 32px;
    text-align: center; color: var(--muted); cursor: pointer;
    transition: border-color .15s, color .15s;
}
.dropzone:hover { border-color: var(--muted); }
.dropzone.drag { border-color: var(--accent); color: var(--text); }
.dropzone strong { color: var(--text); }
.row { display: flex; gap: 12px; margin-top: 14px; flex-wrap: wrap; }
.field { flex: 1; min-width: 140px; }
label { display: block; font-size: 12px; color: var(--muted); margin-bottom: 5px; }
input, select, button, textarea {
    width: 100%; padding: 10px 12px; border-radius: var(--radius);
    border: 1px solid var(--border); background: var(--panel-2); color: var(--text);
    font-size: 14px; font-family: inherit;
}
input:focus, select:focus, textarea:focus {
    outline: none; border-color: var(--accent);
}
/* Chrome paints a remembered value on its own pale background, which on a
   dark form turns the field white and leaves everything sitting on it - the
   reveal control included - looking like it belongs to a different page.
   The background cannot be set on an autofilled field, so it is covered with
   an inset shadow, which can. */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus {
    -webkit-text-fill-color: var(--text);
    -webkit-box-shadow: 0 0 0 1000px var(--panel-2) inset;
    box-shadow: 0 0 0 1000px var(--panel-2) inset;
    caret-color: var(--text);
    transition: background-color 9999s ease-in-out 0s;
}
button {
    background: var(--accent); color: var(--accent-ink); border: none;
    font-weight: 600; cursor: pointer; margin-top: 14px;
}
button:hover { background: var(--accent-hover); }
button:disabled { background: var(--border); color: var(--muted); cursor: not-allowed; }
button:focus-visible, a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
#fileInput { display: none; }
#status { font-size: 13px; color: var(--muted); margin-top: 10px; }
#status.ok { color: var(--ok); }
#status.err { color: var(--err); }
#log {
    background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius); padding: 10px;
    font-family: var(--mono); font-size: 12px; color: var(--muted); max-height: 160px;
    overflow-y: auto; white-space: pre-wrap; margin-top: 10px; display: none;
}
#result { display: none; }
.actions { display: flex; gap: 10px; margin-top: 12px; flex-wrap: wrap; }
.actions button { margin-top: 0; }

.cover-maker {
    margin-top: 20px; padding-top: 18px; border-top: 1px solid var(--border);
}
/* A <details> summary is a list-item by default, with a marker that lands in
   the wrong place next to two stacked lines. */
.cover-maker > summary {
    list-style: none; cursor: pointer; padding: 10px 12px;
    border: 1px solid var(--border); border-radius: var(--radius);
    background: var(--panel-2);
    display: flex; flex-direction: column; gap: 2px;
}
.cover-maker > summary::-webkit-details-marker { display: none; }
.cover-maker > summary:hover { border-color: var(--muted); }
.cover-summary-title { font-size: 14px; font-weight: 600; }
.cover-summary-note { font-size: 12px; color: var(--muted); }
/* The affordance the hidden marker used to carry. */
.cover-summary-title::after { content: "  +"; color: var(--muted); font-weight: 400; }
.cover-maker[open] > summary { margin-bottom: 14px; }
.cover-maker[open] .cover-summary-title::after { content: "  -"; }
.cover-maker h3 { font-size: 15px; margin: 0 0 4px; }
.cover-hint { color: var(--muted); font-size: 12px; margin: 0 0 12px; }
.cover-edit-group {
    border: 1px solid var(--border-soft); border-radius: var(--radius); padding: 10px 12px; margin-bottom: 10px;
}
.cover-group-label { font-size: 11px; text-transform: uppercase; letter-spacing: .04em; margin-bottom: 6px; }
.cover-edit-group input[type="text"] { margin-bottom: 10px; }

.cover-controls {
    display: flex; gap: 14px; flex-wrap: wrap; align-items: center;
}
.cover-controls select { width: auto; flex: 0 0 auto; }
.cover-swatch-label, .cover-size-label {
    display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--muted); margin: 0;
}
.cover-swatch-label input[type="color"] {
    width: 40px; height: 40px; padding: 2px; cursor: pointer;
}
.cover-size-label input[type="range"] { width: 120px; }

.cover-stage {
    position: relative; width: 100%; max-width: 260px; aspect-ratio: 9 / 16;
    margin: 0 auto; border-radius: var(--radius); overflow: hidden; border: 1px solid var(--border);
    background: var(--bg); touch-action: none;
}
.cover-stage-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.cover-block {
    position: absolute; left: 50%; top: 85%; transform: translate(-50%, -50%);
    display: flex; flex-direction: column; align-items: center; gap: 6px;
    max-width: calc(100% - 28px); cursor: grab; user-select: none; touch-action: none;
}
.cover-block:active { cursor: grabbing; }
.cover-title {
    display: flex; flex-wrap: wrap; justify-content: center; gap: 6px 8px; line-height: 1.15;
}
.cover-word {
    color: #fff; -webkit-text-stroke: 1px rgba(0,0,0,.7); text-shadow: 0 2px 6px rgba(0,0,0,.6);
    cursor: pointer; white-space: nowrap;
    /* Padding grows the tap target without growing the visible glyph - the
       negative margin cancels it back out so word spacing on the cover
       image itself doesn't visibly widen. */
    padding: 10px 4px; margin: -10px -4px;
}
.cover-word.is-active { outline: 2px dashed var(--accent); outline-offset: 4px; border-radius: 4px; }
.cover-subtitle {
    color: #fff; text-shadow: 0 2px 6px rgba(0,0,0,.6); text-align: center;
}
.cover-subtitle:empty { display: none; }

.cover-word-popover {
    margin-top: 12px; background: var(--panel-2); border: 1px solid var(--border);
    border-radius: var(--radius); padding: 10px 12px; max-width: 260px; margin-left: auto; margin-right: auto;
}
.cwp-label { display: block; font-size: 12px; color: var(--muted); margin-bottom: 8px; }
.cwp-row { display: flex; gap: 6px; align-items: center; margin-bottom: 8px; flex-wrap: wrap; }
.cwp-row:last-child { margin-bottom: 0; }
.cwp-swatch {
    width: 32px; height: 32px; padding: 0; border-radius: 50%; border: 1px solid var(--border);
    cursor: pointer; margin-top: 0;
}
.cwp-row input[type="color"] { width: 32px; height: 32px; padding: 0; border-radius: 50%; overflow: hidden; }
.cwp-size, #coverWordReset {
    margin-top: 0; padding: 5px 10px; font-size: 12px; background: var(--bg); color: var(--text);
    border: 1px solid var(--border);
}
.cwp-size:hover, #coverWordReset:hover { background: var(--panel); }
.cwp-size.is-active { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }

.cover-preview {
    margin-top: 16px; display: flex; flex-direction: column; align-items: center; gap: 10px;
}
/* The download sizes to its label rather than stretching: it sits under a
   180px image, and a full-width button beside one reads as a stray. */
.cover-preview a { width: auto; margin: 0; }
.cover-preview img {
    width: 180px; border-radius: var(--radius); border: 1px solid var(--border);
    cursor: zoom-in; transition: border-color .15s;
}
.cover-preview img:hover, .cover-preview img:focus-visible { border-color: var(--accent); }
.cover-preview a {
    display: inline-flex; align-items: center; justify-content: center; gap: 8px;
    background: var(--accent); color: var(--accent-ink);
    text-decoration: none; font-weight: 600; font-size: 13px; padding: 9px 16px; border-radius: var(--radius);
}
.cover-preview a:hover { background: var(--accent-hover); }

.image-lightbox {
    position: fixed; inset: 0; z-index: 200; background: rgba(0,0,0,.85);
    display: flex; align-items: center; justify-content: center; padding: 40px 20px;
}
.image-lightbox[hidden] { display: none; }
.image-lightbox img {
    max-width: min(90vw, 480px); max-height: 90vh; border-radius: var(--radius-lg);
    box-shadow: 0 20px 60px rgba(0,0,0,.5);
}
.image-lightbox-close {
    position: absolute; top: 20px; right: 24px; width: auto; margin: 0;
    background: var(--panel-2); color: var(--text); border: 1px solid var(--border);
    border-radius: 50%; width: 40px; height: 40px; font-size: 22px; line-height: 1; padding: 0;
}
.image-lightbox-close:hover { background: var(--panel); }

.ai-cover {
    margin-top: 20px; padding-top: 18px; border-top: 1px dashed var(--border);
}
.ai-cover h4 { font-size: 14px; margin: 0 0 4px; }
.ai-cover .cover-hint { margin-bottom: 12px; }
.ai-cover select { margin-bottom: 10px; }

.ai-cover-prompt {
    resize: vertical; min-height: 44px; margin-bottom: 10px;
}

.ai-cover-results {
    margin-top: 16px; display: flex; flex-wrap: wrap; gap: 16px;
}
.ai-cover-results .cover-preview { margin-top: 0; }
.ai-cover-style-tag { font-size: 11px; color: var(--muted); }

table { width: 100%; border-collapse: collapse; font-size: 13px; }
table th, table td { text-align: left; padding: 10px 8px; border-bottom: 1px solid var(--border-soft); }
table th {
    color: var(--muted); font-weight: 600; font-size: 11px;
    letter-spacing: .08em; text-transform: uppercase; border-bottom-color: var(--border);
}
table td { font-variant-numeric: tabular-nums; }

.flash {
    background: var(--panel-2); border: 1px solid var(--border); border-left: 3px solid var(--accent);
    color: var(--text); padding: 11px 14px; border-radius: var(--radius);
    margin-bottom: 16px; font-size: 13px;
}
.flash.err { border-left-color: var(--err); color: var(--err); }

.pricing-head { text-align: center; margin-bottom: 28px; }
.pricing-head .sub { margin-bottom: 0; }
.pack-grid {
    position: relative; left: 50%; transform: translateX(-50%);
    width: min(1000px, calc(100vw - 40px));
    display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 16px; align-items: stretch;
}
.pack {
    position: relative; display: flex; flex-direction: column;
    transition: transform .15s ease, border-color .15s ease, box-shadow .15s ease;
}
.pack:hover {
    transform: translateY(-3px); border-color: var(--accent);
    box-shadow: 0 14px 32px -16px rgba(245, 197, 24, .45);
}
.pack-featured { border-color: var(--accent); }
.pack-badge {
    position: absolute; top: -11px; left: 50%; transform: translateX(-50%);
    background: var(--accent); color: var(--accent-ink); font-size: 11px; font-weight: 700;
    padding: 3px 12px; border-radius: 999px; white-space: nowrap;
}
.pack-name {
    font-size: 12px; font-weight: 600; letter-spacing: .12em;
    text-transform: uppercase; color: var(--muted);
}
.pack-featured .pack-name { color: var(--accent); }
.pack-minutes { font-family: var(--mono); font-size: 14px; color: var(--text); }
/* The number buyers actually understand, so it carries the same weight as the
   minutes rather than sitting under them as a footnote. */
.pack-videos { font-size: 14px; font-weight: 600; color: var(--text); margin-top: 2px; }
.pack-list {
    list-style: none; padding: 14px 0 0; margin: 14px 0 18px;
    border-top: 1px solid var(--border-soft); flex: 1;
    display: flex; flex-direction: column; gap: 7px;
    font-size: 13px; color: var(--muted);
}
.pack-list li::before { content: "✓"; color: var(--ok); margin-right: 8px; }

.credit-note { text-align: center; font-size: 12px; color: var(--muted); margin: 22px 0 0; }
.compare-strip {
    display: flex; justify-content: center; gap: 26px; flex-wrap: wrap;
    margin: 18px 0 32px; padding: 14px; font-size: 13px; color: var(--muted);
    border-top: 1px solid var(--border-soft); border-bottom: 1px solid var(--border-soft);
}
.compare-strip .ours { color: var(--accent); font-weight: 600; }

.faq details { border-bottom: 1px solid var(--border-soft); padding: 12px 0; }
.faq details:last-child { border-bottom: 0; }
.faq summary { cursor: pointer; font-size: 14px; font-weight: 600; list-style: none; }
.faq summary::before { content: "+"; color: var(--accent); margin-right: 10px; }
.faq details[open] summary::before { content: "−"; }
.faq p { margin: 10px 0 0 24px; font-size: 13px; color: var(--muted); line-height: 1.65; }
.pack .price { font-family: var(--mono); font-size: 34px; font-weight: 500; letter-spacing: -.02em; margin: 8px 0 10px; }

.pack .buy {
    display: block; width: 100%; text-align: center; background: var(--accent);
    color: var(--accent-ink); text-decoration: none; padding: 11px; border-radius: var(--radius);
    font-weight: 600; border: none; font-size: 14px; cursor: pointer; margin-top: 0;
}
.pack .buy:hover { background: var(--accent-hover); }
.pack a.buy.disabled { background: var(--border); color: var(--muted); pointer-events: none; }

.legal h2 { font-size: 16px; margin-top: 24px; }
.legal p, .legal li { font-size: 14px; color: var(--muted); line-height: 1.65; }

/* These four rules used to name .auth-form, which no template has carried for
   a long time, so the line under the form rendered at the browser's default
   size with a default underline while the rest of the card sat at 13px. */
.auth-card .switch {
    text-align: center; margin-top: 18px; font-size: 13px; color: var(--muted);
}
.auth-card .switch a { color: var(--accent); text-decoration: none; font-weight: 500; }
.auth-card .switch a:hover { text-decoration: underline; }

.style-gallery {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 14px; margin-top: 10px;
}
.style-card {
    display: block; background: var(--panel-2); border: 2px solid var(--border);
    border-radius: var(--radius-lg); padding: 10px; cursor: pointer;
    transition: border-color .15s ease, transform .15s ease;
}
.style-card:hover { border-color: var(--muted); transform: translateY(-2px); }
.style-card input { display: none; }
.style-card.selected, .style-card:has(input:checked) {
    border-color: var(--accent); background: var(--panel);
}
.style-card-name { font-size: 13px; font-weight: 600; margin-top: 10px; }
.style-card-desc { font-size: 11px; color: var(--muted); margin-top: 2px; }

#cueList {
    max-height: 260px; overflow-y: auto; border: 1px solid var(--border);
    border-radius: var(--radius); padding: 8px; background: var(--bg);
}
.cue-row { display: flex; gap: 10px; align-items: flex-start; padding: 6px 4px; }
.cue-row + .cue-row { border-top: 1px solid var(--border-soft); }
.cue-time {
    font-family: var(--mono); font-size: 11px; color: var(--muted); width: 90px;
    flex-shrink: 0; padding-top: 10px;
}
.cue-text { flex: 1; resize: vertical; min-height: 34px; }

/* Reel-style animated preview */
.reel-preview {
    position: relative; width: 100%; aspect-ratio: 9 / 16; border-radius: 12px;
    overflow: hidden; display: flex; align-items: flex-end; justify-content: center;
    box-shadow: 0 2px 10px rgba(0,0,0,.35);
}
/* The real render of each style, sitting over the CSS mock-up beneath it. It
   is transparent until it has something to show, so a card never flashes black
   while the file loads - and if the file never loads, the mock-up is still
   there and the card still reads correctly. */
.style-clip {
    position: absolute; inset: 0; width: 100%; height: 100%;
    object-fit: cover; z-index: 2; opacity: 0;
    transition: opacity .35s ease; pointer-events: none;
}
.style-clip.is-playing { opacity: 1; }

.reel-preview::before {
    content: ""; position: absolute; inset: 0;
    background: linear-gradient(to bottom, rgba(0,0,0,0) 55%, rgba(0,0,0,.55) 100%);
}
.grad-1 { background: linear-gradient(160deg, #3a2b52, #17202e); }
.grad-2 { background: linear-gradient(160deg, #2b3a52, #1a1420); }
.grad-3 { background: linear-gradient(160deg, #402438, #14202a); }
.grad-4 { background: linear-gradient(160deg, #23334a, #241a30); }
.grad-5 { background: linear-gradient(160deg, #2f2a45, #1a2a24); }
.grad-6 { background: linear-gradient(160deg, #33202a, #1c2436); }
.reel-icon { color: rgba(255,255,255,.85); font-size: 13px; line-height: 1; }
.reel-caption-wrap {
    position: relative; z-index: 3; width: 100%; padding: 0 8px 14px; text-align: center;
    display: flex; justify-content: center; min-height: 22px; flex-wrap: wrap; gap: 4px;
}
.reel-caption { font-size: 12px; line-height: 1.3; text-align: center; }

/* The illustrated vlog scenes and the eleven caption keyframes that lived
   here are gone with the styles they imitated. A drawing of a caption is a
   guess at what the renderer does; the gallery shows real renders now. */

.flow-note {
    font-size: 13px; color: var(--muted); background: var(--panel); border: 1px solid var(--border);
    border-radius: 8px; padding: 10px 12px; margin-top: 12px; line-height: 1.5;
}
.flow-note strong { color: var(--text); }

.showcase-wrap { display: flex; align-items: center; gap: 8px; margin-top: 10px; }
.showcase-track {
    display: flex; gap: 14px; overflow-x: auto; scroll-snap-type: x mandatory;
    scroll-behavior: smooth; padding: 4px 2px 10px;
}
.showcase-slide { flex: 0 0 150px; scroll-snap-align: start; }
.showcase-name { font-size: 12px; font-weight: 600; text-align: center; margin-top: 8px; color: var(--muted); }
.showcase-arrow {
    flex-shrink: 0; width: 32px; height: 32px; border-radius: 50%; border: 1px solid var(--border);
    background: var(--panel-2); color: var(--text); cursor: pointer; font-size: 13px; padding: 0;
}
.showcase-arrow:hover { background: var(--accent); color: var(--accent-ink); }

.safe-zone-grid {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 16px; margin-top: 8px;
}
.safe-zone-card {
    display: block; background: var(--panel-2); border: 2px solid var(--border); border-radius: 10px;
    padding: 10px; text-decoration: none; color: var(--text); transition: border-color .15s;
}
.safe-zone-card:hover { border-color: var(--accent-hover); }
.safe-zone-thumb {
    background: var(--bg); border-radius: 6px; aspect-ratio: 9/16; overflow: hidden;
    display: flex; align-items: center; justify-content: center;
}
.safe-zone-thumb img { width: 100%; height: 100%; object-fit: contain; }
.safe-zone-card-name { font-size: 13px; font-weight: 600; margin-top: 8px; }
.safe-zone-card-dims { font-size: 11px; color: var(--muted); margin-top: 2px; }
.safe-zone-preview {
    background: var(--bg); border-radius: 8px; max-width: 320px; margin: 0 auto; overflow: hidden;
}
.safe-zone-preview img { width: 100%; display: block; }

#batchList { margin-top: 14px; border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
.batch-row {
    display: flex; align-items: center; gap: 12px; padding: 8px 12px; font-size: 13px;
    background: var(--panel);
}
.batch-row + .batch-row { border-top: 1px solid var(--border); }
.batch-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.batch-status { color: var(--muted); flex-shrink: 0; }
.batch-row button { width: auto; margin-top: 0; padding: 6px 12px; font-size: 12px; flex-shrink: 0; }

.stat-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 14px; margin: 16px 0 24px; }
.stat-card { background: var(--panel); border: 1px solid var(--border); border-radius: 10px; padding: 16px; }
.stat-value { font-size: 26px; font-weight: 700; }
.stat-label { font-size: 12px; color: var(--muted); margin-top: 4px; }

.bar-list { display: flex; flex-direction: column; gap: 10px; }
.bar-row { display: flex; align-items: center; gap: 10px; font-size: 13px; }
.bar-label { width: 160px; flex-shrink: 0; }
.bar-track { flex: 1; background: var(--panel); border-radius: 4px; height: 14px; overflow: hidden; }
.bar-fill { background: var(--accent); height: 100%; border-radius: 4px; }
.bar-count { width: 30px; text-align: right; color: var(--muted); flex-shrink: 0; }

/* ------------------------------------------------------------- editor
   Three columns: what was said, what it looks like, how it should look.
   The panel breaks out of the 720px reading column it sits in, because a
   video preview squeezed into a text width defeats its purpose. */
#editPanel {
    position: relative; left: 50%; transform: translateX(-50%);
    width: min(1400px, calc(100vw - 40px));
}
.editor {
    display: grid; grid-template-columns: 300px minmax(0, 1fr) 320px;
    gap: 16px; align-items: start;
}
.editor-col {
    background: var(--panel); border: 1px solid var(--border);
    border-radius: var(--radius-lg); padding: 16px;
}
.editor-head {
    font-size: 13px; font-weight: 600; margin: 0 0 12px;
    display: flex; flex-direction: column; gap: 2px;
}
.editor-head span { font-size: 11px; font-weight: 400; color: var(--muted); }
.editor-transcript #cueList { max-height: 560px; border: none; background: transparent; padding: 0; }
.editor-styles .style-gallery { grid-template-columns: repeat(2, 1fr); max-height: 520px; overflow-y: auto; }

.editor-stage { display: flex; flex-direction: column; }
.position-toggle {
    display: inline-flex; gap: 2px; align-self: center; margin-bottom: 14px;
    background: var(--panel-2); border: 1px solid var(--border);
    border-radius: var(--radius); padding: 3px;
}
/* Shared by every segmented tab/toggle control in the app (this one, the
   cover-mode switch below, and .history-tab on the account page) - same
   size, same padding, same accent highlight, so a control that does the
   same job looks and taps the same everywhere. min-height 44px is a real
   touch-target minimum, not a design flourish: at the old 6-7px vertical
   padding these came out around 30px tall, tight enough to fat-finger the
   neighboring tab on a phone. */
.position-toggle button {
    width: auto; margin: 0; padding: 6px 16px; font-size: 13px; font-weight: 500;
    min-height: 44px; display: inline-flex; align-items: center; justify-content: center;
    background: transparent; color: var(--muted); border-radius: 6px;
}
.position-toggle button:hover { background: transparent; color: var(--text); }
.position-toggle button.on { background: var(--accent); color: var(--accent-ink); }

.cover-mode-tabs {
    display: inline-flex; gap: 2px; margin-bottom: 14px;
    background: var(--panel-2); border: 1px solid var(--border);
    border-radius: var(--radius); padding: 3px;
}
.cover-mode-tab {
    width: auto; margin: 0; padding: 7px 18px; font-size: 13px; font-weight: 500;
    min-height: 44px; display: inline-flex; align-items: center; justify-content: center;
    background: transparent; color: var(--muted); border-radius: 6px;
}
.cover-mode-tab:hover { background: transparent; color: var(--text); }
.cover-mode-tab.on { background: var(--accent); color: var(--accent-ink); }
.cover-mode-panel[hidden] { display: none; }

.preview-stage {
    /* Sized from its height, not its width. With width:100% and a max-height,
       a tall clip hit the height cap while keeping the full width, so the box
       came out wider than the video: the picture letterboxed inside it and
       the caption - which spans the box - ran out over the black either side.
       Driving the box from the height means it always matches the frame, and
       the caption sits where it will once ffmpeg burns it in. The ratio is
       9/16 until the clip reports its own. */
    position: relative; aspect-ratio: 9 / 16;
    height: 62vh; width: auto; max-width: 100%;
    margin: 0 auto; background: #000; border-radius: var(--radius-lg); overflow: hidden;
    display: flex; align-items: center; justify-content: center;
}
.preview-stage video { width: 100%; height: 100%; object-fit: contain; display: block; }
.preview-buffering {
    position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
    background: rgba(0,0,0,.35); pointer-events: none;
}
.preview-buffering .spinner { width: 26px; height: 26px; flex: 0 0 26px; border-width: 3px; }
.preview-buffering[hidden] { display: none; }
.preview-empty {
    display: flex; align-items: center; justify-content: center;
    color: var(--muted); font-size: 13px; text-align: center; padding: 20px;
}
.caption-overlay {
    position: absolute; left: 0; right: 0; padding: 0 8%;
    display: flex; align-items: center; justify-content: center; text-align: center;
    font-weight: 700; font-size: 20px; line-height: 1.35; color: #fff;
    text-shadow: 0 2px 0 #000, 0 -2px 0 #000, 2px 0 0 #000, -2px 0 0 #000,
                 0 0 6px rgba(0,0,0,.6);
    pointer-events: none;
}
/* Placed by script from a fraction of the frame, and centred on it, so the
   caption can sit anywhere rather than at one of three heights. */
.caption-overlay { top: 86%; transform: translateY(-50%); }
.caption-overlay { pointer-events: auto; cursor: grab; touch-action: none; }
.caption-overlay:active { cursor: grabbing; }
/* A thing that can be dragged has to look like one. The outline appears on
   hover, stays while it is being moved, and pulses once when the editor opens
   so it is noticed at all - a permanent border round every caption would be
   reading the editor's furniture instead of the words. */
.caption-overlay.is-dragging,
.caption-overlay:hover {
    outline: 2px dashed var(--accent); outline-offset: 6px; border-radius: 6px;
}
.caption-overlay.is-inviting {
    outline: 2px dashed var(--accent); outline-offset: 6px; border-radius: 6px;
    animation: drag-invite 1.3s ease-in-out 2;
}
@keyframes drag-invite {
    0%, 100% { outline-color: transparent; }
    50%      { outline-color: var(--accent); }
}

.drag-hint {
    display: flex; align-items: center; gap: 8px;
    margin: 10px 0 0; padding: 8px 10px;
    font-size: 12px; color: var(--text);
    background: var(--panel-2); border: 1px dashed var(--border);
    border-radius: var(--radius);
}
.drag-hint .i { width: 16px; height: 16px; flex: 0 0 16px; color: var(--accent); }

@media (prefers-reduced-motion: reduce) {
    .caption-overlay.is-inviting { animation: none; }
}
.caption-overlay.is-highlighted { color: var(--cap-accent, #fff); }
/* One element per word, so the preview can spring each one in the way the
   render does. inline-block keeps the transform working and holds the word's
   place in the line while it is still at zero - without that the line
   re-centres on every syllable. */
/* Matches WORD_GAP_EM in captions.py, so the preview spaces words the way the
   render does. */
/* Rows, not wrapping. flex-wrap here was a second implementation of the
   renderer's line breaking written in CSS, and it disagreed with the real one
   - the same Arabic cue showed on two lines in the editor and came back as
   one in the exported video. The server now says where the lines break and
   this stacks them; the wrap is kept as the fallback for the moment before
   the first answer arrives. */
.caption-overlay { gap: .30em; flex-wrap: wrap; }
.caption-overlay:has(.caption-line) { flex-direction: column; flex-wrap: nowrap; }
.caption-line {
    display: flex; gap: .30em; flex-wrap: nowrap;
    justify-content: center; align-items: baseline;
}
.cap-word { display: inline-block; transform-origin: center; will-change: transform; }

.preview-controls { display: flex; align-items: center; gap: 12px; margin-top: 14px; }
.preview-controls button {
    width: 38px; height: 38px; flex: 0 0 38px; margin: 0; padding: 0;
    border-radius: 999px; font-size: 12px;
}
#scrub {
    flex: 1; width: auto; padding: 0; background: transparent; border: none;
    accent-color: var(--accent); cursor: pointer;
}
.preview-time { font-family: var(--mono); font-size: 12px; color: var(--muted); white-space: nowrap; }
/* The shared control rule would make this full width and 14px tall padding;
   next to a 38px round play button it has to be small and only as wide as its
   own text. */
.preview-rate {
    flex: 0 0 auto; width: auto; padding: 5px 6px; font-size: 12px;
    font-family: var(--mono); color: var(--muted); cursor: pointer;
}
.preview-rate:hover { color: var(--text); }

/* Two buttons that stamped the current time were not a trim control, they
   were a way to describe one. A range you can see and drag is the thing
   itself: the part being kept is lit, the part being thrown away is not, and
   the answer to "what am I about to export" is the shape on the screen. */
.trim { margin-top: 12px; }
.trim-track {
    position: relative; height: 34px; border-radius: var(--radius);
    background: var(--panel-2); border: 1px solid var(--border);
    cursor: pointer; touch-action: none; overflow: hidden;
}
.trim-keep {
    position: absolute; top: 0; bottom: 0;
    background: rgba(245, 197, 24, .16);
    border-left: 2px solid var(--accent); border-right: 2px solid var(--accent);
}
.trim-playhead {
    position: absolute; top: 0; bottom: 0; width: 2px; background: var(--text);
    opacity: .85; pointer-events: none; transform: translateX(-1px);
}
/* Wider than it looks: the visible grip is 10px but the target is 22px, so it
   can be caught with a thumb as well as a mouse. */
.trim-handle {
    position: absolute; top: 0; bottom: 0; width: 22px; margin: 0; padding: 0;
    transform: translateX(-11px);
    background: none; border: none; border-radius: 0; cursor: ew-resize;
}
.trim-handle::before {
    content: ""; position: absolute; top: 6px; bottom: 6px; left: 6px;
    width: 10px; border-radius: 3px; background: var(--accent);
    box-shadow: 0 1px 4px rgba(0, 0, 0, .5);
}
.trim-handle:hover::before { background: var(--accent-hover); }
.trim-handle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.trim-foot {
    display: flex; align-items: center; justify-content: space-between;
    gap: 10px; margin-top: 8px;
}
.trim-foot button { flex: 0 0 auto; width: auto; margin: 0; }
.trim-readout { font-family: var(--mono); font-size: 12px; color: var(--muted); }
/* Once something is actually being cut, the readout stops being a note and
   starts being a fact about the file they are about to pay to render. */
.trim-readout.is-set { color: var(--accent); }

/* The one paid feature people asked for by name. A plain ghost button reads
   as a utility beside Copy and Save; the accent outline says there is
   something behind it without shouting as loudly as the download. */
/* button.ghost.ai-open, not .ai-open or button.ai-open: this class rides on
   the same element as .ghost, whose rule is both equally specific and further
   down the file - so anything less than two classes loses on source order and
   the accent silently never lands. Twice bitten here; a browser test now
   measures the colour rather than trusting the selector to have won. */
button.ghost.ai-open { border-color: var(--accent); color: var(--accent); }
button.ghost.ai-open:hover:not(:disabled) { background: rgba(245, 197, 24, .10); }
button.ghost.ai-open:disabled { border-color: var(--border); color: var(--muted); }

.copy-gen-upsell {
    margin: 12px 0 0; font-size: 12px; color: var(--muted); line-height: 1.5;
}
.linklike {
    width: auto; margin: 0; padding: 0; border: none; background: none;
    color: var(--accent); font-size: 12px; font-weight: 600;
    text-decoration: underline; cursor: pointer;
}
.linklike:hover { background: none; color: var(--accent-hover); }

/* Typeface, size, colour and position sit here together. Laid out as a wrapping
   row with a real gap: the row is switched to flex from script, so without this
   the controls butt against each other and read as one confused block. */
#exportFormatRow { margin-top: 14px; flex-wrap: wrap; gap: 14px 18px; align-items: flex-start; }
#exportFormatRow .field { min-width: 0; flex: 1 1 150px; margin: 0; }
#exportFormatRow select { width: 100%; }

.export-bar {
    position: sticky; bottom: 0; z-index: 5; margin-top: 16px; padding: 14px 16px;
    display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap;
    background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius-lg);
}
.export-cost { font-size: 13px; color: var(--muted); }
.export-actions { display: flex; align-items: center; gap: 10px; }
.export-actions select { width: auto; min-width: 210px; }
.export-actions button { width: auto; margin: 0; padding: 10px 24px; }

@media (max-width: 1080px) {
    #editPanel { position: static; left: auto; transform: none; width: 100%; }
    .editor { grid-template-columns: 1fr; }
    .editor-styles .style-gallery { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); max-height: none; }
    .preview-stage { height: auto; width: min(340px, 100%); }
}
/* --------------------------------------------------- work in progress
   A job that takes half a minute needs to say so. The raw ffmpeg log used
   to be the status message, filename and all; it is still one click away
   for whoever needs it, and out of the way for everyone else. */
.work-state {
    display: flex; align-items: center; gap: 10px;
    font-size: 13px; color: var(--muted); margin-top: 12px; min-height: 20px;
}
.work-state.ok { color: var(--ok); }
.work-state.err { color: var(--err); }
.spinner {
    /* inline-block (not the default inline) so width/height apply even
       inside a plain button, not just the flex .work-state rows this was
       first built for */
    display: inline-block; vertical-align: -2px;
    width: 14px; height: 14px; flex: 0 0 14px; border-radius: 50%;
    border: 2px solid var(--border); border-top-color: var(--accent);
    animation: spin .7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .spinner { animation-duration: 3s; } }

.work-details { margin-top: 10px; }
.work-details summary {
    font-size: 12px; color: var(--muted); cursor: pointer; list-style: none;
    display: inline-flex; align-items: center; gap: 6px;
}
.work-details summary::before { content: "▸"; font-size: 10px; }
.work-details[open] summary::before { content: "▾"; }
.work-details summary:hover { color: var(--text); }
.work-details #renderLog, .work-details #log {
    display: block; margin-top: 8px; max-height: 200px;
}

/* ------------------------------------------------------------ auth pages
   The form sits beside the thing it unlocks, running the app's own caption
   styles on a stand-in clip rather than a stock photograph. */
.auth-split {
    display: grid; grid-template-columns: 300px 1fr; gap: 32px;
    align-items: center; margin: 40px auto; max-width: 760px;
}
.auth-promo img {
    width: 100%; height: auto; display: block; aspect-ratio: 9 / 16;
    object-fit: cover; border-radius: var(--radius-lg);
    border: 1px solid var(--border);
}
.auth-card {
    background: var(--panel); border: 1px solid var(--border);
    border-radius: var(--radius-lg); padding: 28px;
}
.auth-logo { display: block; width: 52px; height: 52px; margin: 0 auto 16px; }
.auth-card h1 { font-size: 22px; margin-bottom: 4px; }
.auth-lede { color: var(--muted); font-size: 13px; margin: 0 0 20px; }
.auth-card .opt { color: var(--muted); font-weight: 400; }
.auth-card .hint { color: var(--muted); font-size: 11px; margin-top: 5px; }
.trial-pill {
    margin-top: 16px; padding: 8px 12px; border-radius: var(--radius);
    background: rgba(245, 197, 24, .12); border: 1px solid rgba(245, 197, 24, .35);
    color: var(--accent); font-size: 12px; font-weight: 500; text-align: center;
}
.auth-divider {
    display: flex; align-items: center; gap: 10px; margin: 18px 0;
    color: var(--muted); font-size: 12px;
}
.auth-divider::before, .auth-divider::after {
    content: ""; flex: 1; height: 1px; background: var(--border);
}
.google-btn {
    display: flex; align-items: center; justify-content: center; gap: 10px;
    width: 100%; padding: 10px; border-radius: var(--radius);
    border: 1px solid var(--border); background: var(--panel-2);
    color: var(--text); font-weight: 600; font-size: 14px;
    text-decoration: none; box-sizing: border-box;
}
.google-btn:hover { background: var(--panel); border-color: var(--muted); }
.google-btn svg { width: 18px; height: 18px; flex: none; }
/* Choosing a typeface. A native <option> will not render in the face it
   names on every platform, and the face is the whole of what is being chosen,
   so the list is drawn by hand and each name is set in its own face - in its
   own alphabet, for the alphabets that have one. The <select> is still there
   and still holds the value; it is only out of sight. */
.visually-hidden {
    position: absolute; width: 1px; height: 1px; margin: -1px;
    padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
.font-picker { position: relative; }
.font-current {
    width: 100%; margin: 0; display: flex; align-items: center; gap: 10px;
    text-align: left; background: var(--panel-2); color: var(--text);
    border: 1px solid var(--border); font-weight: 400;
}
.font-current:hover { border-color: var(--muted); }
.font-current svg { width: 16px; height: 16px; margin-left: auto; flex: none; }
.font-sample {
    font-size: 19px; line-height: 1.35;
    /* The specimen is the point of the row, so it gets the room. It is also
       the one thing here that must never be clipped mid-letter: Arabic joins,
       and a cut stroke reads as a different word. */
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    max-width: 42%; flex: none;
}
.font-meta { color: var(--muted); font-size: 12px; overflow: hidden; text-overflow: ellipsis; }
.font-list {
    position: absolute; z-index: 40; left: 0; right: 0; top: calc(100% + 4px);
    margin: 0; padding: 4px; list-style: none; max-height: 320px; overflow-y: auto;
    background: var(--panel); border: 1px solid var(--border);
    border-radius: var(--radius); box-shadow: 0 12px 32px rgba(0, 0, 0, .45);
}
.font-option {
    display: flex; align-items: center; gap: 10px;
    padding: 8px 10px; border-radius: var(--radius); cursor: pointer;
}
.font-option:hover, .font-option:focus { background: var(--panel-2); outline: none; }
.font-option:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.font-option[aria-selected="true"] { background: var(--panel-2); }
.font-option[aria-selected="true"] .font-sample { color: var(--accent); }

/* Show the password. Typing a long one blind into a dark field is where
   people give up and reach for the reset link, so the field offers to show
   what was typed. The control sits inside the input rather than beside it,
   which is where hands and eyes both expect it. */
/* flex, not just relative. An input is inline, so a plain block wrapper keeps
   a few pixels of descender space under it - and centring the control on the
   wrapper then sits it that much below the middle of the field, which is
   exactly what it looked like. */
.pw-field { position: relative; display: flex; }
.pw-field input { width: 100%; padding-right: 46px; }
.pw-toggle {
    /* Inset from the edges rather than filling the field, so it reads as a
       control sitting in the field instead of a second border on it. Centred
       on the field's own height, which a full-height button only looked like
       until the field grew a line. */
    position: absolute; top: 50%; right: 6px; transform: translateY(-50%);
    /* margin:0 explicitly. This is a <button>, and the shared button rule
       gives every one of them margin-top:14px - which on an absolutely
       positioned element still pushes it down, and put the eye 14px below the
       middle of the field. */
    width: 32px; height: 32px; margin: 0;
    display: flex; align-items: center; justify-content: center;
    background: none; border: 0; padding: 0; cursor: pointer;
    color: var(--muted); border-radius: var(--radius);
    transition: color .15s ease, background-color .15s ease;
}
.pw-toggle:hover { color: var(--text); background: rgba(255, 255, 255, .06); }
/* Showing is a state, not a click - it stays lit while the password is out,
   so nobody leaves it revealed without noticing. */
.pw-toggle[aria-pressed="true"] { color: var(--accent); }
.pw-toggle:focus-visible {
    outline: 2px solid var(--accent); outline-offset: 1px; color: var(--text);
}
.pw-toggle svg { width: 19px; height: 19px; }
.pw-toggle .off { display: none; }
.pw-toggle[aria-pressed="true"] .on { display: none; }
.pw-toggle[aria-pressed="true"] .off { display: block; }

/* A row for the things beside the password field - the reset link today. */
.auth-aside {
    display: flex; justify-content: flex-end; margin-top: 8px;
    font-size: 12px;
}
.auth-aside a { color: var(--muted); text-decoration: none; }
.auth-aside a:hover { color: var(--accent); }

/* What the account gets, said in three lines rather than a paragraph nobody
   reads on the way to a form. */
.auth-benefits { list-style: none; margin: 0 0 20px; padding: 0; display: grid; gap: 8px; }
.auth-benefits li {
    display: flex; align-items: flex-start; gap: 8px;
    font-size: 13px; color: var(--muted); line-height: 1.45;
}
.auth-benefits svg { width: 15px; height: 15px; flex: none; margin-top: 2px; color: var(--accent); }

/* Logging in and signing up are the same card seen from two sides, so moving
   between them turns it over rather than replacing the page. Both faces are
   in both pages: the URL stays real, a failed submit comes back with the right
   side up, and with no script at all the links are ordinary links to ordinary
   pages - the flip is the enhancement, never the mechanism.

   Nothing below applies until script adds .is-flippable, so a browser that
   never runs it shows one card and no half-turned geometry. */
.auth-flip { perspective: 1800px; }
.auth-flip .auth-face[hidden] { display: none; }

.auth-flip.is-flippable .auth-face[hidden] { display: block; }
.auth-flip.is-flippable .auth-flip-inner {
    position: relative;
    transform-style: preserve-3d;
    transition: transform .62s cubic-bezier(.4, .16, .2, 1);
}
.auth-flip.is-flippable .auth-face {
    /* Both faces are taken out of flow so the card has one height rather than
       the sum of two, and the height is set from the face that is showing. */
    position: absolute; inset: 0 0 auto 0;
    backface-visibility: hidden; -webkit-backface-visibility: hidden;
}
.auth-flip.is-flippable .auth-face[data-face="signup"] { transform: rotateY(180deg); }
.auth-flip.is-flippable[data-face="signup"] .auth-flip-inner { transform: rotateY(180deg); }
.auth-flip.is-flippable {
    transition: height .62s cubic-bezier(.4, .16, .2, 1);
}
/* The face turned away is not just invisible, it is out of reach: a tab press
   must not land in a form nobody can see. */
.auth-flip.is-flippable .auth-face[aria-hidden="true"] { pointer-events: none; }

@media (prefers-reduced-motion: reduce) {
    .auth-flip.is-flippable .auth-flip-inner,
    .auth-flip.is-flippable { transition: none; }
}

/* The card arrives rather than appearing, and its rows follow it in. Small
   distances and one short curve: this is a login screen, and a customer who
   comes back daily should stop noticing it by the third visit. */
@keyframes auth-rise {
    from { opacity: 0; transform: translateY(12px); }
    to   { opacity: 1; transform: none; }
}
/* Never on .auth-face. A face carries the flip's own rotateY, and a CSS
   animation beats a normal declaration - so this rise was resetting the
   turned-away face to flat, putting it in front of the one that should be
   showing. It was inert, being the inactive side, so the login page showed an
   unclickable signup form. The rise goes on the container instead, which
   holds no transform of its own. */
.auth-flip, .auth-promo, .auth-card:not(.auth-face) {
    animation: auth-rise .5s cubic-bezier(.22, .8, .3, 1) both;
}
.auth-promo { animation-delay: .06s; }
.auth-card > * { animation: auth-rise .45s cubic-bezier(.22, .8, .3, 1) both; }
.auth-card > *:nth-child(1) { animation-delay: .10s; }
.auth-card > *:nth-child(2) { animation-delay: .16s; }
.auth-card > *:nth-child(3) { animation-delay: .22s; }
.auth-card > *:nth-child(4) { animation-delay: .28s; }
.auth-card > *:nth-child(5) { animation-delay: .34s; }
.auth-card > *:nth-child(6) { animation-delay: .40s; }
.auth-card > *:nth-child(7) { animation-delay: .46s; }
@media (prefers-reduced-motion: reduce) {
    .auth-flip, .auth-promo, .auth-card, .auth-card > * { animation: none; }
}

@media (max-width: 720px) {
    .auth-split { grid-template-columns: 1fr; max-width: 400px; }
    .auth-promo { display: none; }
}

/* --------------------------------------------------------- account page */
.balance-card { display: flex; align-items: center; gap: 24px; flex-wrap: wrap; }
.balance-main { flex: 1; min-width: 240px; }
.balance-label { font-size: 12px; color: var(--muted); }
.balance-number {
    font-family: var(--mono); font-size: 40px; line-height: 1.1; color: var(--accent);
    display: flex; align-items: baseline; gap: 8px; margin: 2px 0 14px;
}
.balance-number span { font-family: var(--font); font-size: 13px; color: var(--muted); }
.balance-bar { height: 8px; border-radius: 999px; background: var(--border); overflow: hidden; }
.balance-bar span { display: block; height: 100%; background: var(--accent); border-radius: 999px; }
.balance-scale {
    display: flex; justify-content: space-between; margin-top: 6px;
    font-family: var(--mono); font-size: 11px; color: var(--muted);
}
.buy-more {
    background: var(--accent); color: var(--accent-ink); text-decoration: none;
    padding: 11px 22px; border-radius: var(--radius); font-weight: 600; font-size: 14px;
}
.buy-more:hover { background: var(--accent-hover); }

.history-controls {
    display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin-bottom: 14px;
}
.history-tabs { display: flex; gap: 6px; }
.history-tab {
    margin-top: 0; width: auto; padding: 7px 14px; font-size: 13px; font-weight: 600;
    min-height: 44px; display: inline-flex; align-items: center; justify-content: center;
    background: var(--panel-2); color: var(--muted); border: 1px solid var(--border);
}
.history-tab:hover { background: var(--panel); color: var(--text); }
.history-tab.is-active { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
#historySearch { flex: 1; min-width: 160px; width: auto; }
#historySort { width: auto; flex: 0 0 auto; }

.table-scroll { overflow-x: auto; }
td.nowrap { white-space: nowrap; }
td.filename { font-family: var(--mono); font-size: 12px; max-width: 260px; overflow-wrap: anywhere; }
.chip {
    display: inline-block; padding: 3px 10px; border-radius: 999px;
    font-size: 11px; font-weight: 600; white-space: nowrap;
}
.chip-ok   { background: rgba(62, 207, 126, .14); color: var(--ok); }
.chip-work { background: rgba(245, 197, 24, .14); color: var(--accent); }
.chip-err  { background: rgba(255, 180, 171, .14); color: var(--err); }
.chip-note { font-size: 11px; color: var(--muted); margin-top: 4px; }

.password-form { display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap; }
.password-form .field { flex: 1; min-width: 200px; }
.password-form button { width: auto; margin: 0; padding: 10px 20px; }

/* ------------------------------------------------------------------- nav */
nav { gap: 24px; }
.nav-links { display: flex; align-items: center; gap: 20px; margin-right: auto; }
.nav-links a { color: var(--muted); text-decoration: none; font-size: 14px; }
.nav-links a:hover { color: var(--text); }
.nav-actions { display: flex; align-items: center; gap: 14px; }
.nav-actions > a { color: var(--muted); text-decoration: none; font-size: 14px; }
.nav-actions > a:hover { color: var(--text); }
.nav-cta {
    background: var(--accent); color: var(--accent-ink) !important; font-weight: 600;
    padding: 8px 16px; border-radius: var(--radius); font-size: 13px; white-space: nowrap;
}
.nav-cta:hover { background: var(--accent-hover); }
/* <details> rather than a scripted dropdown: it opens, closes and takes focus
   on its own, including without JavaScript. */
.nav-menu { position: relative; }
.nav-menu summary { list-style: none; cursor: pointer; }
.nav-menu summary::-webkit-details-marker { display: none; }
.avatar {
    display: flex; align-items: center; justify-content: center;
    width: 32px; height: 32px; border-radius: 50%;
    background: var(--panel-2); border: 1px solid var(--border);
    font-size: 13px; font-weight: 600; color: var(--text);
}
.nav-menu[open] .avatar { border-color: var(--accent); }
.nav-menu-panel {
    position: absolute; right: 0; top: calc(100% + 10px); z-index: 40; min-width: 210px;
    background: var(--panel); border: 1px solid var(--border); border-radius: var(--radius-lg);
    padding: 8px; display: flex; flex-direction: column;
    box-shadow: 0 16px 40px -16px rgba(0, 0, 0, .8);
}
.nav-menu-email {
    font-size: 12px; color: var(--muted); padding: 8px 10px;
    border-bottom: 1px solid var(--border-soft); margin-bottom: 4px; overflow-wrap: anywhere;
}
.nav-menu-panel a {
    padding: 8px 10px; border-radius: 6px; font-size: 13px;
    color: var(--text); text-decoration: none;
}
.nav-menu-panel a:hover { background: var(--panel-2); }

/* --------------------------------------------------------------- settings */
.settings-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 16px; }
.settings-grid .panel { margin-bottom: 0; }
.stack { display: flex; flex-direction: column; gap: 12px; }
.stack button { margin-top: 2px; }
input:disabled { color: var(--muted); cursor: not-allowed; }
@media (max-width: 820px) {
    nav { flex-wrap: wrap; }
    .nav-links { order: 3; width: 100%; margin-right: 0; }
}
.actions-cell { display: flex; gap: 8px; flex-wrap: wrap; }
.actions-cell a {
    display: inline-flex; align-items: center; padding: 4px 10px;
    border-radius: var(--radius); border: 1px solid var(--border);
    background: var(--panel-2); color: var(--text);
    font-size: 12px; font-weight: 600; text-decoration: none; white-space: nowrap;
}
.actions-cell a:hover { border-color: var(--accent); color: var(--accent); }
.actions-cell a[href*="?job="] {
    background: rgba(245, 197, 24, .12); border-color: rgba(245, 197, 24, .35);
    color: var(--accent);
}
.expired { color: var(--muted); font-style: italic; }

/* --------------------------------------------------- out of minutes
   The packs come to the person rather than sending them to a pricing page
   and losing the video they were part-way through. */
.credit-modal { display: none; }
.credit-modal.is-open {
    display: flex; position: fixed; inset: 0; z-index: 60;
    align-items: center; justify-content: center; padding: 20px;
    background: rgba(8, 10, 14, .74); backdrop-filter: blur(5px);
    animation: credit-fade .18s ease-out;
}
.credit-card {
    position: relative; width: min(760px, 100%); max-height: 92vh; overflow-y: auto;
    background: var(--panel); border: 1px solid var(--border);
    border-radius: var(--radius-lg); padding: 30px;
    box-shadow: 0 30px 70px -24px rgba(0, 0, 0, .85);
    animation: credit-rise .24s cubic-bezier(.2, .8, .3, 1);
}
.credit-card h2 { font-size: 20px; margin: 0 0 6px; }
.credit-reason { color: var(--muted); font-size: 13px; margin: 0 0 20px; }
.credit-close {
    position: absolute; top: 14px; right: 14px; width: 40px; height: 40px;
    margin: 0; padding: 0; border-radius: 50%; font-size: 18px; line-height: 1;
    display: flex; align-items: center; justify-content: center;
    background: transparent; color: var(--muted); border: 1px solid var(--border);
}
.credit-close:hover { background: var(--panel-2); color: var(--text); }

.credit-packs { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.credit-pack {
    position: relative; width: auto; margin: 0; padding: 20px 14px 16px; text-align: center;
    display: flex; flex-direction: column; align-items: center; gap: 3px;
    background: var(--panel-2); color: var(--text);
    border: 1px solid var(--border); border-radius: var(--radius-lg);
    transition: transform .15s ease, border-color .15s ease, background .15s ease,
                box-shadow .15s ease;
}
.cp-badge {
    position: absolute; top: -10px; left: 50%; transform: translateX(-50%);
    background: var(--accent); color: var(--accent-ink); font-size: 10px; font-weight: 700;
    padding: 2px 10px; border-radius: 999px; white-space: nowrap;
}
.cp-minutes { font-family: var(--mono); font-size: 12px; color: var(--muted); }
.cp-list {
    display: flex; flex-direction: column; gap: 5px; margin: 12px 0 14px;
    padding-top: 12px; border-top: 1px solid var(--border); width: 100%;
    font-size: 11px; color: var(--muted); text-align: left;
}
.cp-list span::before { content: "✓"; color: var(--ok); margin-right: 6px; }
.cp-cta {
    margin-top: auto; width: 100%; padding: 8px; border-radius: var(--radius);
    background: var(--border); color: var(--text); font-size: 12px; font-weight: 600;
    transition: background .15s ease, color .15s ease;
}
.credit-pack.is-featured .cp-cta { background: var(--accent); color: var(--accent-ink); }
.credit-pack:hover:not(:disabled) .cp-cta { background: var(--accent); color: var(--accent-ink); }
.credit-pack:hover:not(:disabled) {
    background: var(--panel); border-color: var(--accent); transform: translateY(-3px);
    box-shadow: 0 12px 28px -14px rgba(245, 197, 24, .5);
}
.credit-pack.is-featured { border-color: var(--accent); }
.credit-pack:disabled { opacity: .45; }
.cp-name { font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.cp-price { font-family: var(--mono); font-size: 22px; color: var(--text); }
.cp-videos { font-size: 12px; font-weight: 600; color: var(--text); }

.credit-foot { font-size: 12px; color: var(--muted); text-align: center; margin: 18px 0 0; }
.credit-waiting {
    display: none; align-items: center; justify-content: center; gap: 10px;
    margin-top: 16px; font-size: 13px; color: var(--muted);
}
.credit-waiting.is-on { display: flex; }

@keyframes credit-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes credit-rise {
    from { opacity: 0; transform: translateY(14px) scale(.97); }
    to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
    .credit-modal.is-open, .credit-card { animation: none; }
    .credit-pack { transition: none; }
}
@media (max-width: 520px) { .credit-packs { grid-template-columns: 1fr; } }

/* ------------------------------------------------------------- what is next
   The one screen where somebody has finished rather than being mid-task, so
   it is where the two paid text features are worth offering - each naming its
   price instead of being a button that only says a verb. */
.ai-busy {
    display: flex; align-items: center; gap: 10px; margin: 0 0 10px;
    padding: 10px 12px; border-radius: var(--radius);
    background: var(--panel-2); border: 1px solid var(--accent);
    font-size: 13px; color: var(--text);
}

/* The second half of the pricing page - the top-up packs under the plans. */
.site-footer {
    margin-top: 56px; padding: 24px 20px 32px;
    border-top: 1px solid var(--border-soft); text-align: center;
}
.footer-links {
    display: flex; justify-content: center; flex-wrap: wrap; gap: 10px 22px;
}
.footer-links a { color: var(--muted); text-decoration: none; font-size: 13px; }
.footer-links a:hover { color: var(--text); text-decoration: underline; }
.footer-note { color: var(--muted); font-size: 12px; margin: 14px 0 0; }

/* The legal and support pages. Narrow measure, because these are read rather
   than scanned - and the one part of the product where being hard to read
   looks like hiding something. */
.legal { max-width: 640px; margin: 0 auto; padding: 8px 0 0; }
.legal h1 { margin-bottom: 6px; }
.legal h2 { font-size: 16px; font-weight: 600; margin: 28px 0 8px; }
.legal p { color: var(--text); font-size: 14px; line-height: 1.65; margin: 0 0 12px; }
.legal .sub { color: var(--muted); font-size: 13px; }
.legal a { color: var(--accent); }
.legal-entity { color: var(--muted); font-size: 12px; margin-top: 28px; }
/* Shown, not hidden: a blank where the support email belongs is a
   configuration fault the operator has to be able to see. */
.legal-missing {
    color: var(--err); font-size: 13px;
    border: 1px solid var(--err); border-radius: var(--radius); padding: 10px 12px;
}
.legal-missing code { font-family: var(--mono); font-size: 12px; }

.period-toggle {
    display: flex; justify-content: center; gap: 4px; margin: 0 auto 26px;
    padding: 4px; width: max-content;
    background: var(--panel-2); border: 1px solid var(--border);
    border-radius: 999px;
}
.period-tab {
    width: auto; margin: 0; padding: 8px 18px; border: none; border-radius: 999px;
    background: transparent; color: var(--muted); font-size: 13px; font-weight: 600;
}
.period-tab:hover { background: transparent; color: var(--text); }
.period-tab.on { background: var(--accent); color: var(--accent-ink); }
/* The discount rides on the tab, so the reason to look at annual is on the
   control that switches to it rather than only inside the cards behind it. */
.period-save {
    margin-left: 6px; padding: 2px 7px; border-radius: 999px;
    background: var(--ok); color: var(--accent-ink);
    font-family: var(--mono); font-size: 11px; font-weight: 600;
}
/* Green whichever tab is active. It is a saving, and the one colour on this
   page that means "good" is the one it should keep - swapping it to the
   accent on the active tab made it read as part of the button. */
.period-tab.on .period-save { background: var(--ok); color: var(--accent-ink); }

/* Both panes are rendered; only one is shown. */
.period-pane { display: none; }
.period-pane.on { display: grid; }

.price-unit { font-size: 15px; font-weight: 500; color: var(--muted); }
.price-billed { font-size: 12px; color: var(--muted); margin-bottom: 10px; }
.pricing-free {
    text-align: center; color: var(--muted); font-size: 13px; margin: 22px 0 0;
}

.pricing-h2 {
    font-size: 18px; font-weight: 600; margin: 42px 0 4px; text-align: center;
}
.pricing-sub2 {
    color: var(--muted); font-size: 13px; margin: 0 0 20px; text-align: center;
}

.next-steps {
    margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--border);
}
.next-steps h4 { font-size: 13px; font-weight: 600; margin: 0 0 10px; }
.next-steps-row { display: flex; gap: 8px; flex-wrap: wrap; }
/* .next-steps button.next-step-offer, not .next-step-offer: `button.small`
   sits on the same element, is equally specific and comes later in the file,
   so a single class here loses on source order and the alignment silently
   does nothing. Third time this file has done that. */
.next-steps button.next-step-offer {
    flex: 1 1 200px; width: auto; margin: 0;
    flex-direction: column; align-items: flex-start; justify-content: center;
    gap: 3px; text-align: left; padding: 10px 14px;
}
.next-price { font-family: var(--mono); font-size: 11px; color: var(--accent); }

/* ------------------------------------------------------------- the AI sheet
   Translate and Generate used to sit in the transcript toolbar as two buttons
   with prices in their labels. Behind one button instead, each gets a heading,
   a sentence saying what it does to the transcript, and its price somewhere it
   can be read rather than squeezed into a label. */
.stage-bar {
    display: flex; align-items: center; justify-content: space-between;
    gap: 12px; margin-bottom: 10px; min-height: 30px;
}
.stage-bar button { margin: 0; }
/* Nobody chose the language any more, so what was heard has to be legible -
   quiet, but present. */
.stage-lang { font-size: 12px; color: var(--muted); white-space: nowrap; }
.stage-lang strong { color: var(--text); font-weight: 600; }

.ai-modal {
    display: flex; position: fixed; inset: 0; z-index: 65;
    align-items: center; justify-content: center; padding: 20px;
    background: rgba(8, 10, 14, .74); backdrop-filter: blur(5px);
    animation: credit-fade .18s ease-out;
}
.ai-card {
    position: relative; width: min(460px, 100%); max-height: 92vh; overflow-y: auto;
    background: var(--panel); border: 1px solid var(--border);
    border-radius: var(--radius-lg); padding: 26px;
    box-shadow: 0 30px 70px -24px rgba(0, 0, 0, .85);
    animation: credit-rise .24s cubic-bezier(.2, .8, .3, 1);
}
.ai-card h2 { font-size: 18px; margin: 0 0 6px; }
.ai-sub { color: var(--muted); font-size: 12px; margin: 0 0 20px; }
.ai-action {
    background: var(--panel-2); border: 1px solid var(--border-soft);
    border-radius: var(--radius); padding: 16px; margin-bottom: 12px;
}
.ai-action:last-child { margin-bottom: 0; }
.ai-action-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.ai-action-head h3 { font-size: 14px; font-weight: 600; margin: 0; }
/* The price is the reason this sheet exists: two paid actions whose cost was
   previously a parenthesis inside a button label. */
.ai-price {
    font-family: var(--mono); font-size: 11px; color: var(--accent); white-space: nowrap;
}
.ai-action-note { color: var(--muted); font-size: 12px; line-height: 1.5; margin: 6px 0 12px; }
/* The shared control rule sets width: 100% on both a select and a button, so
   in a flex row each asks for the whole line and the loser collapses - the
   language menu shrank to a bare chevron. Basis comes from content here, and
   only the menu is allowed to grow. */
.ai-action-do { display: flex; gap: 8px; }
.ai-action-do select { flex: 1 1 auto; width: auto; min-width: 0; }
.ai-action-do button { flex: 0 0 auto; width: auto; margin: 0; white-space: nowrap; }
/* A button with nothing beside it is that block's one action, so it takes the
   width and reads as the thing to press. */
.ai-action-do button:only-child { flex: 1 1 auto; width: 100%; }
@media (prefers-reduced-motion: reduce) { .ai-modal, .ai-card { animation: none; } }

/* ---------------------------------------------- asking before doing something
   The browser's own confirm() cannot be styled: it paints Chrome's grey box,
   in Chrome's font, headed "vidcap.onrender.com says", and it freezes the page
   underneath. Being asked to spend credits in a dialog that looks nothing like
   the product is exactly where trust leaks, so the question is asked in the
   app's own voice - same overlay, card and motion as the credits modal above.

   The buttons need margin: 0 because the global button rule sets margin-top,
   which would push them off the row. */
.ask-modal {
    display: flex; position: fixed; inset: 0; z-index: 70;
    align-items: center; justify-content: center; padding: 20px;
    background: rgba(8, 10, 14, .74); backdrop-filter: blur(5px);
    animation: credit-fade .18s ease-out;
}
.ask-card {
    width: min(420px, 100%);
    background: var(--panel); border: 1px solid var(--border);
    border-radius: var(--radius-lg); padding: 26px;
    box-shadow: 0 30px 70px -24px rgba(0, 0, 0, .85);
    animation: credit-rise .24s cubic-bezier(.2, .8, .3, 1);
}
.ask-card h2 { font-size: 17px; margin: 0 0 8px; }
.ask-body { color: var(--muted); font-size: 13px; line-height: 1.55; margin: 0; }
/* The price is the part worth reading twice, so it is not left in the middle
   of a sentence in muted grey. */
.ask-cost {
    display: block; margin-top: 10px; font-family: var(--mono);
    font-size: 12px; color: var(--accent);
}
.ask-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 22px; }
.ask-actions button { margin: 0; }
@media (prefers-reduced-motion: reduce) {
    .ask-modal, .ask-card { animation: none; }
}
@media (max-width: 420px) {
    .ask-actions { flex-direction: column-reverse; }
    .ask-actions button { width: 100%; }
}

@media (prefers-reduced-motion: reduce) {
    .pack, .pack:hover, .style-card, .style-card:hover, .credit-pack { transition: none; transform: none; }
}



/* ------------------------------------------------------- kinetic backdrop
   A grid that bends towards the pointer and rings out from every click. It
   paints the page background itself, so it has to sit under everything and
   take no clicks; content is lifted above it rather than the canvas pushed
   below, which keeps the modals - already fixed and higher - untouched. */
.grid-canvas {
    position: fixed; inset: 0; z-index: 0; display: block;
    pointer-events: none; opacity: 0; transition: opacity .9s ease;
}
.grid-canvas.is-on { opacity: 1; }
nav { position: relative; z-index: 2; }
.wrap { position: relative; z-index: 1; }
/* Panels sit on moving light, so they need a ground of their own rather than
   the transparency they could get away with over a flat colour. */
.panel, .auth-card, .editor-col, .export-bar, .table-scroll {
    background-color: rgba(25, 28, 34, .92);
    backdrop-filter: blur(10px);
}
/* The canvas stays: the script draws it once and stops rather than hiding it,
   so asking for less motion costs the design rather than removing it. */

/* ------------------------------------------------------------- flux loader
   Progress lives under the nav rather than in a dialog over the page: the
   work is visible from anywhere on the page, and nothing is blocked while it
   happens. */
.flux {
    position: sticky; top: 0; z-index: 3;
    height: 0; overflow: hidden; opacity: 0;
    transition: height .3s ease, opacity .3s ease;
    background: var(--panel);
    border-bottom: 1px solid transparent;
}
.flux.is-on { height: 34px; opacity: 1; border-bottom-color: var(--border); }
.flux-track {
    position: relative; height: 3px; background: var(--border); overflow: hidden;
}
.flux-fill {
    display: block; height: 100%; width: 0;
    background: linear-gradient(90deg, var(--accent-hover), var(--accent));
    box-shadow: 0 0 12px rgba(245, 197, 24, .75), 0 0 3px rgba(245, 197, 24, .9);
    transition: width .45s cubic-bezier(.25, .8, .3, 1);
}
/* While a phase has nothing to measure - one API call, no progress events -
   the bar sweeps instead of inventing a number. */
.flux.is-indeterminate .flux-fill {
    width: 34% !important; transition: none;
    animation: flux-sweep 1.5s ease-in-out infinite;
}
@keyframes flux-sweep {
    0%   { transform: translateX(-110%); }
    100% { transform: translateX(330%); }
}
.flux-label {
    display: flex; gap: .5px; padding: 6px 24px;
    font-family: var(--mono); font-size: 11px; letter-spacing: .14em;
    text-transform: uppercase; color: var(--accent);
}
.flux-label span {
    display: inline-block; white-space: pre;
    animation: flux-letter .32s cubic-bezier(.2, .9, .3, 1) both;
}
@keyframes flux-letter {
    from { opacity: 0; transform: translateY(6px) scale(.9); filter: blur(2px); }
    to   { opacity: 1; transform: none; filter: none; }
}
.flux.is-done .flux-label { color: var(--ok); }
.flux.is-done .flux-fill {
    background: var(--ok); box-shadow: 0 0 12px rgba(62, 207, 126, .7);
}
@media (prefers-reduced-motion: reduce) {
    .flux-label span { animation: none; }
    .flux.is-indeterminate .flux-fill { animation: none; width: 100% !important; opacity: .4; }
}

/* The step that follows, given the weight the step just finished used to have. */
.batch-row button.next-step {
    width: auto; margin: 0; padding: 10px 20px; font-size: 14px; white-space: nowrap;
    box-shadow: 0 0 0 0 rgba(245, 197, 24, .55); animation: next-pulse 2.4s ease-out 3;
}
.batch-row button.next-step span { margin-left: 6px; }
@keyframes next-pulse {
    0%   { box-shadow: 0 0 0 0 rgba(245, 197, 24, .55); }
    70%  { box-shadow: 0 0 0 12px rgba(245, 197, 24, 0); }
    100% { box-shadow: 0 0 0 0 rgba(245, 197, 24, 0); }
}
/* Demoted, not hidden: transcribing another file is still allowed, just no
   longer the thing being suggested. */
/* .ghost was scoped to .export-actions and used in six places outside it,
   where it therefore did nothing: Copy, Save and the rest inherited the plain
   button rule and came out in full accent yellow, so four neutral utilities
   read as loudly as Render. The rule it wanted was this one, character for
   character, so the two names now share it. */
button.secondary, button.ghost {
    background: transparent; color: var(--text); border: 1px solid var(--border);
}
button.secondary:hover, button.ghost:hover { background: var(--panel-2); }
@media (prefers-reduced-motion: reduce) { .batch-row button.next-step { animation: none; } }

/* Icons ride with their label rather than sitting beside it, so a button is
   one object and wraps as one. Stroked in currentColor, so they follow the
   button through every state instead of needing their own colour. */
button, .buy, .buy-more, .nav-cta {
    display: inline-flex; align-items: center; justify-content: center; gap: 8px;
}
.i { width: 16px; height: 16px; flex: 0 0 16px; }
.buy .i, .buy-more .i { width: 15px; height: 15px; flex: 0 0 15px; }

/* The step that follows carries the weight. It is the one thing to do next;
   transcribing another file is allowed, not suggested. */
.batch-row button.next-step { padding: 11px 22px; font-weight: 650; }
#goBtn.secondary { font-weight: 500; }

/* What this deploy can actually do, on the admin page. Without reading it
   back, an image built without the renderer looks identical to a working one
   until a customer tries to export. */
.admin-engines {
    font-size: 13px; color: var(--muted); margin: -6px 0 18px;
}
.admin-engines strong { color: var(--text); font-weight: 600; }
.engine-warn { color: var(--err); }
.engine-note { color: var(--muted); }

/* Shown when the server has no styles registered. It says what still works
   rather than leaving an empty box that reads as something failing to load. */
.style-empty {
    font-size: 13px; color: var(--muted); line-height: 1.6;
    border: 1px dashed var(--border); border-radius: var(--radius);
    padding: 14px 16px; margin-top: 12px;
}
.style-empty strong { color: var(--text); font-weight: 600; }

/* The note under the typeface picker: Arabic ignores the choice, and saying so
   next to the control is better than a caption of empty boxes. */
.field-note { font-size: 11px; color: var(--muted); margin-top: 6px; line-height: 1.5; }

/* The text-size slider. Styled explicitly because a bare range input inherits
   the browser's own look, which is the one thing on this panel that would not
   match anything around it. */
#sizeRange, .range {
    width: 100%; margin: 6px 0 0; padding: 0; height: 22px;
    background: transparent; -webkit-appearance: none; appearance: none;
    cursor: pointer;
}
#sizeRange::-webkit-slider-runnable-track, .range::-webkit-slider-runnable-track {
    height: 4px; border-radius: 999px; background: var(--border);
}
#sizeRange::-moz-range-track, .range::-moz-range-track {
    height: 4px; border-radius: 999px; background: var(--border);
}
#sizeRange::-webkit-slider-thumb, .range::-webkit-slider-thumb {
    -webkit-appearance: none; appearance: none; margin-top: -7px;
    width: 18px; height: 18px; border-radius: 999px;
    background: var(--accent); border: none;
}
#sizeRange::-moz-range-thumb, .range::-moz-range-thumb {
    width: 18px; height: 18px; border-radius: 999px;
    background: var(--accent); border: none;
}
#sizeRange:focus-visible, .range:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
/* The reading, next to the label rather than under the slider, so the row
   does not change height as the number goes from 60% to 100%. */
#sizeValue, #wordsValue, #holdValue {
    color: var(--muted); font-weight: 500; font-variant-numeric: tabular-nums;
}

/* ------------------------------------------------- work panel
   Progress sits immediately above the button that started the work, because
   that is where someone is looking after they press it - not pinned to the top
   of the page where they have to go and find it. */
.work-panel {
    margin-top: 14px; padding: 14px 16px;
    background: var(--panel); border: 1px solid var(--border);
    border-radius: var(--radius-lg);
}
.work-panel[hidden] { display: none; }
.work-panel-top {
    display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
}
.work-phase { font-size: 13px; font-weight: 600; color: var(--text); }
/* Tabular figures, so the percentage does not jitter as the digits change. */
.work-pct {
    font-size: 13px; font-weight: 600; color: var(--accent);
    font-variant-numeric: tabular-nums;
}
.work-track {
    position: relative; height: 6px; margin-top: 10px; overflow: hidden;
    background: var(--panel-2); border-radius: 999px;
}
.work-fill {
    display: block; height: 100%; width: 0; border-radius: 999px;
    background: var(--accent); transition: width .35s ease;
}
/* Nothing to report a percentage for - a transcription is an API call with no
   progress to read - so the bar sweeps instead of lying about a number. */
.work-panel.is-waiting .work-fill {
    width: 35%; transition: none;
    animation: work-sweep 1.5s cubic-bezier(.45,0,.55,1) infinite;
}
@keyframes work-sweep {
    0%   { transform: translateX(-110%); }
    100% { transform: translateX(320%); }
}
.work-note {
    margin: 10px 0 0; font-size: 12px; color: var(--muted); line-height: 1.5;
    min-height: 1.5em;
    opacity: 1; transform: translateY(0);
    transition: opacity .45s ease, transform .45s ease;
}
/* The outgoing message fades and lifts before the next one is written, so the
   line changes rather than snapping. */
.work-note.is-fading { opacity: 0; transform: translateY(-4px); }

@media (prefers-reduced-motion: reduce) {
    .work-fill { transition: none; }
    .work-panel.is-waiting .work-fill { animation: none; width: 100%; opacity: .35; }
    .work-note { transition: none; }
}

/* ------------------------------------------------- colour picker
   A bare colour input is a small square that reads as decoration. The swatch,
   the value and a hint make it look like the control it is. */
.colour-pick {
    position: relative; display: flex; align-items: center; gap: 10px;
    margin-top: 6px; padding: 8px 10px; cursor: pointer;
    background: var(--panel-2); border: 1px solid var(--border);
    border-radius: var(--radius);
}
.colour-pick:hover { border-color: var(--muted); }
.colour-swatch {
    width: 22px; height: 22px; flex: 0 0 22px; border-radius: 6px;
    border: 1px solid rgba(255,255,255,.18);
    background: var(--cap-accent, #3ecf7e);
}
.colour-value {
    font-size: 12px; font-weight: 600; color: var(--text);
    font-variant-numeric: tabular-nums; letter-spacing: .02em;
}
.colour-hint { margin-left: auto; font-size: 11px; color: var(--muted); }
/* Kept in the layout and clickable through the label, rather than hidden with
   display:none, which stops a browser opening its picker from the label. */
.colour-pick input[type="color"] {
    position: absolute; inset: 0; width: 100%; height: 100%;
    opacity: 0; cursor: pointer; border: none; padding: 0;
}

/* ------------------------------------------------- transcript views
   The block is for reading and fixing; the timed rows for when it matters
   which line is when. */
.transcript-tools {
    display: flex; align-items: center; justify-content: space-between;
    gap: 12px; margin-bottom: 10px; flex-wrap: wrap;
}
.transcript-tools .position-toggle { margin: 0; }
/* The shared rule gives every button width: 100%, which in a flex row is a
   basis that grows - so Copy stretched across the whole line once Save
   stopped being there to share it with. These size to their own labels. */
.transcript-tools button.small { flex: 0 0 auto; width: auto; }
button.small {
    padding: 6px 12px; font-size: 12px; margin: 0;
    display: inline-flex; align-items: center; gap: 6px;
}
button.small .i { width: 14px; height: 14px; }
#copyTranscriptBtn.is-done, #saveTranscriptBtn.is-done { border-color: var(--accent); color: var(--accent); }

.translate-tool { display: flex; align-items: center; gap: 6px; }
.translate-tool select {
    width: auto; padding: 6px 10px; font-size: 12px; margin: 0;
}
#translateBtn.is-done { border-color: var(--accent); color: var(--accent); }

.copy-gen { margin-top: 12px; }
.copy-gen-result {
    margin-top: 12px; display: flex; flex-direction: column; gap: 10px;
}
/* What stands there instead until something has been generated. An empty
   result block explained nothing; this says what the feature is and leads to
   the button that does it. */
.copy-gen-offer {
    margin-top: 12px; padding: 16px; text-align: center;
    background: var(--panel-2); border: 1px dashed var(--border);
    border-radius: var(--radius);
}
.copy-gen-offer p {
    margin: 0 0 12px; font-size: 12px; color: var(--muted); line-height: 1.55;
}
.copy-gen-offer button { display: inline-flex; }
.copy-gen-field {
    background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius);
    padding: 10px 12px;
}
.copy-gen-field label {
    margin: 0 0 4px; font-size: 11px; text-transform: uppercase; letter-spacing: .04em;
}
.copy-gen-value {
    font-size: 13px; line-height: 1.5; white-space: pre-wrap; word-break: break-word;
}
.copy-gen-copy {
    margin-top: 8px; width: auto; padding: 4px 10px; font-size: 11px;
    background: var(--panel-2); color: var(--text); border: 1px solid var(--border);
}
.copy-gen-copy:hover { background: var(--panel); }
.copy-gen-copy.is-done { border-color: var(--accent); color: var(--accent); }

.transcript-block {
    width: 100%; min-height: 320px; max-height: 52vh; resize: vertical;
    padding: 14px 16px; line-height: 1.7; font-size: 14px;
    background: var(--bg); color: var(--text);
    border: 1px solid var(--border); border-radius: var(--radius);
    font-family: inherit;
}
.transcript-block:focus {
    outline: none; border-color: var(--accent);
}
.transcript-block[hidden] { display: none; }
#cueList[hidden] { display: none; }

/* A note that is a warning rather than an aside - no bundled face can draw
   this script, so the render would produce boxes. */
.field-note.is-warning { color: var(--accent); }
select:disabled { opacity: .5; cursor: not-allowed; }

/* Shown only when the selected style has no live preview - the export is fine,
   the preview is the part that is missing, and saying so beats showing another
   style's motion. */
.preview-gap {
    margin: 10px 0 0; padding: 8px 10px; font-size: 11px; line-height: 1.5;
    color: var(--accent); background: rgba(245,197,24,.08);
    border: 1px dashed rgba(245,197,24,.35); border-radius: var(--radius);
}
.preview-gap[hidden] { display: none; }


/* ------------------------------------------------------ landing page
   What an advert click lands on. Its single job is a signup, so every
   section either shows the product working or asks for the account. */
.lp-hero {
    display: grid; grid-template-columns: 1fr minmax(240px, 320px);
    gap: 48px; align-items: center; padding: 40px 0 64px;
}
.lp-eyebrow {
    font-family: var(--mono); font-size: 12px; letter-spacing: .09em;
    text-transform: uppercase; color: var(--muted); margin: 0 0 14px;
}
.lp-title { font-size: 52px; line-height: 1.05; letter-spacing: -.03em; margin: 0 0 18px; }
/* The proof is in the headline: the word is Arabic, and it is set in a real
   Arabic face rather than described in English. */
.lp-arabic { color: var(--accent); font-family: "cap-Cairo", "cap-Amiri", serif; }
.lp-lede { color: var(--muted); font-size: 17px; line-height: 1.6; max-width: 52ch; margin: 0 0 26px; }
.lp-cta-row { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; }
.lp-cta {
    display: inline-block; background: var(--accent); color: var(--accent-ink);
    font-weight: 700; font-size: 16px; padding: 14px 24px;
    border-radius: var(--radius); text-decoration: none;
}
.lp-cta:hover { background: var(--accent-hover); }
.lp-cta-ghost {
    color: var(--muted); text-decoration: none; font-size: 15px; padding: 14px 4px;
}
.lp-cta-ghost:hover { color: var(--text); }
.lp-fineprint { color: var(--muted); font-size: 13px; margin: 14px 0 0; }

/* A demo frame keeps its shape whether the file exists yet or not, so the
   page does not jump around as the clips are shot and dropped in. */
.lp-demo {
    margin: 0; aspect-ratio: 9 / 16; border-radius: var(--radius-lg);
    overflow: hidden; background: var(--panel); border: 1px solid var(--border);
}
.lp-demo video { width: 100%; height: 100%; object-fit: cover; display: block; }
.lp-demo.is-empty { border-style: dashed; border-color: var(--border); }
.lp-slot {
    height: 100%; display: flex; flex-direction: column; justify-content: center;
    gap: 8px; padding: 22px; text-align: center;
}
.lp-slot-tag {
    font-family: var(--mono); font-size: 10px; letter-spacing: .1em;
    text-transform: uppercase; color: var(--accent);
}
.lp-slot-title { font-weight: 600; font-size: 15px; }
.lp-slot-note { color: var(--muted); font-size: 13px; line-height: 1.5; }

.lp-section { padding: 56px 0; border-top: 1px solid var(--border-soft); }
.lp-h2 { font-size: 28px; letter-spacing: -.02em; margin: 0 0 8px; }
.lp-sub { color: var(--muted); font-size: 15px; margin: 0 0 26px; max-width: 60ch; }
.lp-demo-grid {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 18px;
}
.lp-demo-grid figcaption, .lp-style figcaption {
    font-size: 13px; color: var(--muted); padding-top: 8px; text-align: center;
}
.lp-demo-grid figure { display: grid; }
.lp-styles {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 16px;
}
.lp-style { margin: 0; }
.lp-style video {
    width: 100%; aspect-ratio: 9 / 16; object-fit: cover; display: block;
    border-radius: var(--radius); border: 1px solid var(--border); background: var(--panel);
}
.lp-faces { display: flex; flex-wrap: wrap; gap: 14px 28px; align-items: baseline; }
.lp-faces span { font-size: 34px; line-height: 1.5; direction: rtl; }

.lp-steps { list-style: none; margin: 0; padding: 0; display: grid; gap: 18px; counter-reset: lp; }
.lp-steps li {
    display: grid; grid-template-columns: 34px 1fr; gap: 14px;
    align-items: start; counter-increment: lp;
}
.lp-steps li::before {
    content: counter(lp); font-family: var(--mono); font-weight: 700; font-size: 13px;
    width: 34px; height: 34px; border-radius: 50%; display: grid; place-items: center;
    background: var(--panel-2); color: var(--accent); border: 1px solid var(--border);
}
.lp-steps strong { display: block; }
.lp-steps span { color: var(--muted); font-size: 15px; }

.lp-plans { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; }
.lp-plan {
    position: relative; background: var(--panel); border: 1px solid var(--border);
    border-radius: var(--radius-lg); padding: 24px 20px; display: grid; gap: 5px;
    justify-items: start;
}
.lp-plan.is-featured { border-color: var(--accent); }
.lp-plan-badge {
    position: absolute; top: -10px; left: 20px; background: var(--accent);
    color: var(--accent-ink); font-size: 11px; font-weight: 700; padding: 3px 9px;
    border-radius: 999px;
}
.lp-plan-name { font-weight: 600; }
.lp-plan-price { font-size: 30px; font-weight: 700; letter-spacing: -.02em; }
.lp-plan-credits { color: var(--text); font-size: 14px; }
.lp-plan-videos { color: var(--muted); font-size: 13px; }
.lp-plan-cta {
    margin-top: 12px; color: var(--accent); text-decoration: none;
    font-weight: 600; font-size: 14px;
}
.lp-plan-cta:hover { text-decoration: underline; }

.lp-final { padding: 64px 0 24px; border-top: 1px solid var(--border-soft); text-align: center; }
.lp-final .lp-sub { margin-inline: auto; }

@media (max-width: 820px) {
    .lp-hero { grid-template-columns: 1fr; gap: 32px; }
    .lp-demo-hero { max-width: 300px; }
    .lp-title { font-size: 38px; }
}
