:root{font-family:system-ui,sans-serif;color:#eef3ff;background:#101524}*{box-sizing:border-box}body{margin:0}.topbar{display:flex;justify-content:space-between;align-items:center;padding:18px max(24px,calc((100% - 1100px)/2));background:#171e32}.subtitle{color:#aebbd9;margin-left:12px;font-size:.9rem}.top-actions{display:flex;gap:5px}.page{max-width:1100px;margin:32px auto;padding:0 20px}.card,.privacy-card{border:1px solid #2d3854;border-radius:14px;background:#182139;padding:22px;margin-bottom:18px}.privacy-card{border-color:#226955;background:#102920;color:#d6f6e9}.engine-card{display:grid;grid-template-columns:1fr auto;gap:16px;align-items:center}h1,h2{margin:0 0 10px}h1{font-size:1.25rem}h2{font-size:1rem}.status,.wake-status{grid-column:1/-1;margin:0;color:#b8c6e6}.wake-status{font-size:.86rem}.segmented{display:flex}.segmented button{border-radius:0}.segmented button:first-child{border-radius:8px 0 0 8px}.segmented button:last-child{border-radius:0 8px 8px 0}.workflow{display:grid;grid-template-columns:1fr 1fr minmax(0,260px);gap:20px}.step{min-width:0}input[type=file]{max-width:100%}button,.primary,a{padding:10px 14px;border:1px solid #405078;border-radius:8px;background:#263352;color:#fff;font:inherit;cursor:pointer;text-decoration:none}.primary,.selected{background:#4268e8;border-color:#6687ff}.icon-button{background:transparent;border-color:transparent}.preview{min-height:120px}
/* FIX (live report: "the final output preview does not need to be huge (full width)... just
   like the py [app] we need to calculate if the images are portrait or landscape... we cannot
   lock it by width, then the image might be too big if it is not landscape"). #result-preview
   previously had NO sizing rule at all, so it rendered at its raw pixel resolution (e.g. a
   1024x1536 portrait photo really did show at 1024px wide, and taller still) - the actual
   pipeline's own output canvas is always the same resolution as the target image, so a portrait
   target always produced a huge, mostly-scrolled-off preview. Checked what app.v50.py itself
   does here (its preview_box <img> tags, e.g. `style="max-height: 360px; margin: auto; display:
   block;"`) - it does NOT run any JS/Python orientation branch either: capping ONLY max-height
   (never width) and letting width stay `auto` is the whole technique - the browser's own
   aspect-ratio-preserving scaling does the "is it portrait or landscape" math for free. A
   landscape result ends up wide and 360px tall; a portrait result ends up narrow and 360px tall;
   neither is ever "full width" by default. Ported the same idea, just with a taller cap (this
   page has more room than Gradio's own panel) and a max-width safety net for a narrow mobile
   viewport showing a very wide landscape result. */
#result-preview{display:block;max-height:520px;width:auto;max-width:100%;margin:0 auto;object-fit:contain}
/* RUNDE 60 - the display:block rule right above beats the browser's own default
   [hidden]{display:none} UA rule the exact same way already fixed twice before for `dialog` and
   `.image-picker` (see their own comments) - just never applied HERE, so a video/GIF/WEBP result
   (which has no <img> preview of its own, so this element is meant to stay hidden for it) could
   leave the PREVIOUS run's still-image result visibly on screen even though app.js had already
   set hidden=true on it - see showResult()'s own RUNDE 60 comment in app.js for the live report
   this fixes. */
#result-preview[hidden]{display:none!important}
dialog{color:#eef3ff;background:#182139;border:1px solid #405078;border-radius:14px;max-width:430px;width:calc(100% - 32px)}dialog form{display:grid;gap:17px}dialog header{display:flex;justify-content:space-between}dialog label{display:grid;gap:7px}.check{grid-template-columns:auto 1fr;align-items:center}dialog p{color:#b8c6e6;font-size:.9rem}progress{width:100%;grid-column:1/-1}.model-list{display:grid;gap:9px;margin:18px 0}.model-row{display:flex;justify-content:space-between;gap:18px;padding:14px;border-radius:8px;background:#10182b}.model-row div{display:grid;gap:3px}.model-row div span,.model-state{color:#b8c6e6;font-size:.88rem}@media(max-width:700px){.engine-card,.workflow{grid-template-columns:1fr}.segmented{width:100%}.segmented button{flex:1}.topbar{padding:16px}.subtitle{display:none}.model-row{display:grid}}
.settings-section{border:1px solid #2d3854;border-radius:10px;padding:12px 14px;margin:0;display:grid;gap:12px}.settings-section legend{padding:0 6px;color:#aebbd9;font-size:.85rem;text-transform:uppercase;letter-spacing:.03em}.face-gallery{display:flex;gap:10px;flex-wrap:wrap;margin-top:10px}.face-thumb{border:2px solid transparent;border-radius:8px;padding:0;background:#10182b;cursor:pointer;overflow:hidden;display:block}.face-thumb.selected{border-color:#6687ff}.face-thumb img{display:block;width:84px;height:84px;object-fit:cover}.hint{color:#8092b8;font-size:.82rem;margin:4px 0 0}

/* RUNDE 56 - kollapsbar "Feilsøking"-seksjon (<details> i stedet for <fieldset>, se index.php sin
   egen kommentar der for hvorfor) med ett avkrysningsfelt per debug-lag, hver med en liten
   fargeprikk som viser nøyaktig hvilken farge/linje laget tegnes med på resultatet - gjør det
   lett å koble en boks til det man faktisk ser, i stedet for å måtte huske/gjette. Selve
   .settings-section sin faste padding nullstilles her siden <summary>/.debug-layers-body styrer
   sin egen padding i stedet (samme totale kantavstand, bare fordelt annerledes siden <summary> må
   være klikkbar helt til kanten). */
details.settings-section{padding:0}
details.settings-section>summary{cursor:pointer;padding:12px 14px;color:#aebbd9;font-size:.85rem;text-transform:uppercase;letter-spacing:.03em;list-style:none;user-select:none}
details.settings-section>summary::-webkit-details-marker{display:none}
details.settings-section>summary::before{content:'▸';display:inline-block;width:1em}
details.settings-section[open]>summary::before{content:'▾'}
.debug-layers-body{display:grid;gap:14px;padding:0 14px 14px}
.debug-layer-group{display:grid;gap:6px}
.debug-layer-group h4{margin:0 0 2px;font-size:.78rem;color:#8092b8;text-transform:uppercase;letter-spacing:.02em}
.debug-layer-row span{display:flex;align-items:center;font-size:.88rem}
.debug-swatch{display:inline-block;width:11px;height:11px;border-radius:3px;margin-right:7px;flex-shrink:0;border:1px solid rgba(255,255,255,.25)}

/* Author rules always win over the UA default [hidden]{display:none}, regardless of
   specificity - "dialog label{display:grid}" above was silently overriding hidden=true
   on every collapsible settings row. This restores the hide behaviour explicitly. */
dialog [hidden]{display:none!important}

/* FIX (live report: clicking "Last modell og start" a second time - really, whenever
   #gpu-warning-status/#frame-status/#cancel-button become visible, e.g. a video/GIF job -
   made the "Kilde"/"Mål" buttons fall behind/under the start button). Root cause: .workflow's
   3rd column ("3. Behandle") was sized `auto`, and CSS Grid sizes an `auto` track's max width
   to the MAX-CONTENT (unwrapped, single-line) width of whatever is inside it - not its actual
   wrapped/rendered width. #gpu-warning-status holds a long warning sentence that's normally
   `hidden` (contributes nothing) but becomes visible mid-run; once visible, its unwrapped
   max-content width is far wider than the button itself, so the "auto" column tried to grow
   to fit that single unbroken line, stealing space from the two `1fr` columns beside it (down
   to their own min-content width, which is how the Kilde/Mål controls ended up squeezed behind
   the start button). Was already partly guarded by ".step{min-width:0}" above, but that only
   protects grid ITEMS from blowing out their OWN track - it does nothing to stop a wide item in
   one track from taking space FROM another track. Fixed by bounding the 3rd column's own max
   width (was `auto`, now `minmax(0,260px)`) so it can never grow past a sane cap no matter how
   long the warning/status text inside it gets - that text now simply wraps onto multiple lines
   within the 260px column instead, which is normal, expected behaviour. */

/* FIX (live request: "we should have a similar source image option that the python app had, with
   option for webcam, drop or upload, in a small neat face icon, then a nice small thumbnail
   preview... this should also be for target image just as the py app had (off course without web
   cam etc.)"). See index.php's own comment on the new #source-picker/#target-picker markup and
   app.js's wireImagePicker()/openWebcam() for the full mechanism - this is purely the visual
   side.

   RUNDE 52 (2026-09-17) rewrite, live follow-up after seeing this rendered: the slot (drop hint OR
   thumbnail, never both) is now ONE element (`.image-picker-slot`) instead of two separately-
   positioned siblings the caller had to keep in sync - see index.php/app.js's own RUNDE 52
   comments for the full "why". `.image-picker-drop`/`.image-picker-thumb`/`.image-picker-remove`
   are now all absolutely-stacked children of that one slot, switched by the `[hidden]` attribute
   alone (see the `.image-picker [hidden]` rule just below - THIS is what was actually missing
   before: every one of these three had its own explicit `display:` value, so plain [hidden] alone
   was never enough on its own, exactly the same bug class the dialog fix above already covers). */
.image-picker{display:grid;gap:8px}
.image-picker-slot{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:150px;border:2px dashed #405078;border-radius:10px;background:#10182b;cursor:pointer;overflow:hidden}
.image-picker-slot:hover,.image-picker-slot:focus-visible{border-color:#6687ff}
.image-picker-slot.dragover{border-color:#6687ff;background:#182139}
.image-picker-slot.has-image{border-style:solid;border-color:#2d3854}
/* Author rules always win over the UA default [hidden]{display:none}, regardless of specificity -
   every child here (.image-picker-drop/.image-picker-thumb/.image-picker-remove) sets its own
   `display:` value, which was silently overriding `hidden` the exact same way "dialog
   label{display:grid}" already had to be fixed for above. Without this, the drop-zone hint AND the
   chosen-file thumbnail (or the phantom empty <img>/<video> with no file chosen yet) could show at
   once - live-reported as "the initial window has a preview image missing" (an empty, broken-image
   <img> visible despite hidden) and "the preview can replace the drop image block... we do not
   need both" (both visible together once a file WAS chosen). */
.image-picker [hidden]{display:none!important}
.image-picker-drop{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;padding:12px;text-align:center;color:#8092b8;pointer-events:none}
.image-picker-icon{width:36px;height:36px;fill:none;stroke:#54628c;stroke-width:3;stroke-linecap:round;stroke-linejoin:round}
.image-picker-slot:hover .image-picker-icon,.image-picker-slot:focus-visible .image-picker-icon,.image-picker-slot.dragover .image-picker-icon{stroke:#6687ff}
.image-picker-hint{font-size:.82rem}
.image-picker-link{font-size:.82rem;color:#8fa4d8;text-decoration:underline;text-underline-offset:2px}
.image-picker-thumb{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
/* RUNDE 59 - fallback shown when a preview element (the <img> or the target-only <video>) fails to
   decode its file (unsupported codec/container, or a corrupt file) - see index.php's own RUNDE 59
   comment on #target-thumb-video for why this exists instead of leaving a blank/black box. Deliberately
   styled like the empty drop-hint (same icon size/layout) rather than a scary error message - the
   slot is still fully usable (still shows the filename, still clickable to pick a different file). */
.image-picker-thumb-fallback{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;padding:12px;text-align:center;color:#8092b8;background:#10182b}
.image-picker-thumb-fallback .image-picker-icon{stroke:#6687ff}
.image-picker-thumb-fallback-name{font-size:.82rem;color:#8fa4d8;word-break:break-all;text-decoration:underline;text-underline-offset:2px}
/* RUNDE 59 - live report: cancelling a running video job ("Avbryt") and then immediately picking a
   different target file could leave stale state on screen (the old job's own "Ramme X av Y" counter
   kept showing, since nothing stopped the picker from being used mid-job/mid-cancel while the old
   run was still finishing "assembling what's done so far" in the background). Rather than trying to
   coordinate two overlapping async flows, app.js's setFilePickersBusy() now simply disables both
   pickers for the whole run (start click -> finally), the same way #start-button already was -
   pointer-events:none blocks click/drag/drop on the whole slot (including the "x" remove button,
   a descendant, since pointer-events is inherited), and app.js's own click/drop/keydown handlers
   also check input.disabled directly as a second guard (keyboard Enter/Space isn't pointer-events).*/
.image-picker-slot.busy{opacity:.55;cursor:not-allowed;pointer-events:none}
/* Small circular "x" overlaid on the thumbnail (RUNDE 52) - replaces the old separate "Fjern"
   button below the picker. stopPropagation() in app.js keeps a click here from also reopening the
   file dialog (the slot itself is the click target for everything else in it). */
.image-picker-remove{position:absolute;top:6px;right:6px;width:28px;height:28px;padding:0;border-radius:50%;background:rgba(16,24,43,.85);border:1px solid #405078;color:#fff;font-size:.85rem;line-height:1;display:flex;align-items:center;justify-content:center}
.image-picker-remove:hover{background:#263352;border-color:#6687ff}
/* Webcam trigger (source only) is a small icon-only control BELOW the slot, not part of it (it
   isn't a drop/click-to-upload target itself) - RUNDE 52 swapped the colorful 📷 emoji for a
   monochrome (currentColor) SVG icon per live feedback ("this will look better with a single
   color cam/webcam icon... instead of the color camera icon"). */
.image-picker-webcam-btn{display:flex;align-items:center;justify-content:center;gap:6px;background:transparent;border:1px solid #405078;border-radius:8px;padding:8px 10px;font-size:.85rem}
.image-picker-webcam-btn:hover{border-color:#6687ff}
.webcam-icon{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
#webcam-video{display:block;width:100%;max-height:360px;border-radius:10px;background:#000;object-fit:contain}
/* RUNDE 52 - the face gallery moved from a standalone full-width card (#face-section used to sit
   between the whole "1/2/3" row and the output preview) to right under the target picker's own
   slot instead, per live request ("in the block below target preview, we can list all the found
   faces... remove the long section below, and keep that for the final output instead"). Just a
   thin divider + a little breathing room - .face-gallery itself (thumbnail sizing/layout) is
   unchanged, defined already a few lines up. */
.face-section-inline{margin-top:12px;padding-top:12px;border-top:1px solid #2d3854}
.face-section-inline p{margin:0 0 8px}
