/* ---------- device stage: a film (or an image) inside a device, shared by every case study page ----------
   Markup is written by make_stage.py, behaviour is in device-stage.js, the device numbers come from mockup-frames/<device>.json.
   The film sits exactly on the screen rectangle of the device (absolute, percent based, computed from the numbers below),
   UNDER a frame image whose screen is transparent. Swapping the frame means swapping the image and its numbers, nothing else.

   Numbers on .ds (all in frame pixels, set inline by make_stage.py and updated by the script when a new frame is found):
   --ds-fw --ds-fh            frame size
   --ds-sx --ds-sy --ds-sw --ds-sh   screen rectangle inside the frame
   --ds-r1 --ds-r2 --ds-r3 --ds-r4   screen corner radii: top left, top right, bottom right, bottom left
   --ds-ga --ds-gb --ds-dir   the ground behind the film before it has loaded: the mean colour of two film edges
   --ds-pos                   where a film whose shape differs from the screen rests in it (default: centred; on the laptop on the bottom edge) */

.ds {
	--ds-pad: clamp(12px, 2.4vw, 30px);
	position: relative; box-sizing: border-box; display: grid; place-items: center; place-self: stretch;
	width: 100%; height: 100%; min-height: var(--ds-min, clamp(250px, 58vw, 600px)); padding: var(--ds-pad);
	max-height: max(420px, calc(100vh - 150px));            /* in a hero field that is taller than the window the stage keeps to the part that is seen, so device and control are in view together */
	container-type: size;                                   /* the device is fitted into this box by width or by height, whichever is smaller */
}
.ds[data-device="phone"] { --ds-min: clamp(460px, 124vw, 640px); }
.ds[data-device="laptop"] { --ds-min: clamp(280px, 72vw, 600px); }   /* on a phone the laptop takes the whole width: the stage needs the height of the device plus its control (reference pass, 11 October 2026) */
.ds[data-device="desktop"] { --ds-min: clamp(280px, 70vw, 640px); }
.ds[data-device="tablet"] { --ds-min: clamp(270px, 66vw, 620px); }

/* the device and its control are one unit: the control sits under the right corner of the device, so it is in view whenever the device is */
.ds-unit { display: flex; flex-direction: column; align-items: flex-end; gap: 8px; --ds-ctl: 52px; }
.ds-device {
	position: relative; flex: none;
	/* fitted by width or by height into the stage, and never taller than the window leaves room for, so the whole device is seen at once */
	width: min(100cqw, calc((100cqh - var(--ds-ctl)) * var(--ds-fw) / var(--ds-fh)), calc(max(340px, 100vh - 290px) * var(--ds-fw) / var(--ds-fh)));
	aspect-ratio: var(--ds-fw) / var(--ds-fh);              /* reserved before anything loads: no layout shift */
}
.ds-screen {
	position: absolute; overflow: hidden; isolation: isolate;   /* its own layer: the rounded corners clip a playing film in every browser */
	left: calc(var(--ds-sx) / var(--ds-fw) * 100%); top: calc(var(--ds-sy) / var(--ds-fh) * 100%);
	width: calc(var(--ds-sw) / var(--ds-fw) * 100%); height: calc(var(--ds-sh) / var(--ds-fh) * 100%);
	border-radius: calc(var(--ds-r1, 0) / var(--ds-sw) * 100%) calc(var(--ds-r2, 0) / var(--ds-sw) * 100%) calc(var(--ds-r3, 0) / var(--ds-sw) * 100%) calc(var(--ds-r4, 0) / var(--ds-sw) * 100%)
		/ calc(var(--ds-r1, 0) / var(--ds-sh) * 100%) calc(var(--ds-r2, 0) / var(--ds-sh) * 100%) calc(var(--ds-r3, 0) / var(--ds-sh) * 100%) calc(var(--ds-r4, 0) / var(--ds-sh) * 100%);
	background: linear-gradient(var(--ds-dir, to bottom), var(--ds-ga, #000) 50%, var(--ds-gb, #000) 50%);
	box-shadow: 0 0 0 1px var(--ds-bezel, #08080a);         /* a hair of bezel colour under the frame edge, so rounding never opens a gap */
}
/* How a film sits in the screen cutout (11 October 2026, after Kait's two notes: films must fill the screen, and nothing of a film may be cut off).
   1. The WHOLE film frame is always shown, never stretched (object-fit: contain). A recording is an interface: a cut edge takes away a logo or a menu.
   2. Where the film's shape differs from the screen by a hair (1.2 percent or less, the phone recordings), it covers the cutout exactly: data-fit="cover",
      set by make_stage.py and, for a page written earlier, by device-stage.js. What is lost is one or two pixels of margin.
   3. Where it differs by more, the spare room takes a ground that continues the film's own edge colours (the canvas .ds-ground, drawn by the script), so no
      black or foreign bar shows. On the laptop the film stands on the BOTTOM edge and all spare room is at the top, where the camera housing sits: the
      housing then covers ground, never the film. Other devices centre the film.
   The only way to lose the ground entirely is a recording made at the shape of the screen (laptop 3456 x 2234, desktop 16:9, tablet 4:3).
   --ds-pos on .ds sets another resting point; data-fit="cover" or "contain" on .ds forces one of the two. */
.ds .ds-film, .ds .ds-still { position: absolute; inset: 0; display: block; width: 100%; height: 100%; max-width: none; object-fit: contain; object-position: var(--ds-pos, 50% 50%); border-radius: 0; box-shadow: none; }
.ds[data-device="laptop"] .ds-film, .ds[data-device="laptop"] .ds-still { object-position: var(--ds-pos, 50% 100%); }
.ds[data-fit="cover"] .ds-film, .ds[data-fit="cover"] .ds-still { object-fit: cover; object-position: var(--ds-pos, 50% 50%); }
/* the still lies over the film whenever the film is not running (not started, paused, out of view, autoplay refused, reduced motion).
   Without the script it is not shown, so the browser's own poster and controls stay reachable. */
.ds .ds-still { display: none; pointer-events: none; transition: opacity .45s ease; }
.ds.is-on .ds-still { display: block; }
.ds.is-film .ds-still { opacity: 0; }
/* the ground behind a film that does not fill the screen: a tiny canvas that holds the two film edges, stretched over the screen (the script draws it) */
.ds .ds-ground { position: absolute; inset: 0; display: block; width: 100%; height: 100%; max-width: none; }
.ds-frame { position: absolute; inset: 0; background: center / 100% 100% no-repeat; pointer-events: none; }

.ds .ds-toggle {
	position: relative; z-index: 2; flex: none; min-height: 44px; min-width: 44px; padding: 0 15px; border: 0; border-radius: 8px; cursor: pointer;
	background: var(--ds-ctl-bg, var(--pb-on-accent, #141414)); color: var(--ds-ctl-fg, var(--pb-on-accent-inv, #FFFFFF));
	font: inherit; font-size: .8125rem; font-weight: 500; letter-spacing: 0;
}
.ds .ds-toggle[hidden] { display: none; }
.ds .ds-toggle:focus-visible { outline: 3px solid var(--ds-ctl-fg, var(--pb-on-accent-inv, #FFFFFF)); outline-offset: -6px; }

/* a stage that is not the hero field brings its own brand ground (the same colour and line grid as the hero field of the kit) */
.ds--block {
	height: clamp(300px, 60vw, min(760px, max(360px, 100vh - 130px))); min-height: 0; max-height: none; border-radius: var(--pb-radius, 6px); overflow: hidden;
	background-color: var(--pb-accent, #DDDDDD);
	background-image: linear-gradient(rgba(var(--pb-on-accent-rgb, 0, 0, 0), .075) 1px, transparent 1px), linear-gradient(90deg, rgba(var(--pb-on-accent-rgb, 0, 0, 0), .075) 1px, transparent 1px);
	background-size: 56px 56px; background-position: center;
}
.ds--block[data-device="phone"] { height: clamp(480px, 124vw, 760px); }

/* no device: a finished film in a plain rounded frame, as wide as its column */
.ds--plain { display: block; place-items: normal; max-height: none; width: min(100%, calc(max(320px, 100vh - 150px) * var(--ds-fw) / var(--ds-fh))); height: auto; min-height: 0; margin-left: auto; margin-right: auto; padding: 0; container-type: normal; }
.ds--plain .ds-unit { display: block; position: relative; width: 100%; }
.ds--plain .ds-device { width: 100%; }
.ds--plain .ds-toggle { position: absolute; right: 12px; bottom: 12px; }
.ds--plain .ds-screen { border-radius: var(--ds-plain-radius, 14px); box-shadow: 0 0 0 1px var(--kc-hair, rgba(0, 0, 0, .14)); }
.ds--plain .ds-toggle { --ds-ctl-bg: #141414; --ds-ctl-fg: #FFFFFF; }

@media (prefers-reduced-motion: reduce) { .ds .ds-still { transition: none; } }

/* under the fact line (slot after-top) a stage keeps the distance the page keeps between its big parts */
.ds--block, .ds--plain { margin-bottom: clamp(48px, 8vw, 96px); }

/* ---------- Kait, 11 October 2026: "transparent backgrounds, I want the work to stand out" ----------
   No coloured ground behind a device, on any page: the hero field and a block stage show the page's own neutral surface. The device is large in its
   field (no inner padding), and the Pause and Play control takes the neutral ink of the page instead of a brand pair that needed the brand ground.
   These rules live here, in the linked file, so the pages that are never rebuilt (real ciphertext) follow without a change of their markup. */
.kcase .kc-hero:has(> .ds[data-device-stage]) { background-color: transparent; background-image: none; }
.ds--block { background-color: transparent; background-image: none; border-radius: 0; overflow: visible; }
.ds:not(.ds--plain) { --ds-pad: 0px; }
.ds:not(.ds--plain) .ds-toggle { --ds-ctl-bg: var(--kc-text, #141414); --ds-ctl-fg: var(--kc-bg, #FFFFFF); }
.ds:not(.ds--plain) .ds-toggle:focus-visible { outline: 2px solid var(--kc-text, #141414); outline-offset: 2px; }
/* Reference pass, 11 October 2026: a device in the hero field takes the whole width of the field, as the picture does on the Sovara Labs page.
   Several slot styles gave every custom hero an inner padding for the coloured ground that is gone; with a stage inside, that padding only made the device small. */
.kcase .kc-hero:has(> .ds[data-device-stage]) { padding: 0; }

/* ---------- a STILL in a device frame, no film and no script (reference pass, 11 October 2026: no raw screenshot is dropped on a page) ----------
   <span class="dsf dsf--phone"><img ...></span>. The picture sits on the screen rectangle of the frame, the frame image lies over it. The numbers are
   those of mockup-frames/<device>.json on 11 October 2026 (shots.so frames at half size); a new frame means new numbers here. */
.dsf { position: relative; display: block; width: 100%; aspect-ratio: var(--ds-fw) / var(--ds-fh); }
.kcase .kc-fig .dsf > img, .dsf > img {
	position: absolute; left: calc(var(--ds-sx) / var(--ds-fw) * 100%); top: calc(var(--ds-sy) / var(--ds-fh) * 100%);
	width: calc(var(--ds-sw) / var(--ds-fw) * 100%); height: calc(var(--ds-sh) / var(--ds-fh) * 100%); max-width: none; object-fit: cover; object-position: 50% 0;
	border-radius: calc(var(--ds-r) / var(--ds-sw) * 100%) / calc(var(--ds-r) / var(--ds-sh) * 100%); box-shadow: none; background: #08080a;
}
.dsf::after { content: ''; position: absolute; inset: 0; background: var(--dsf-frame) center / 100% 100% no-repeat; pointer-events: none; }
.dsf--phone { --ds-fw: 824; --ds-fh: 1685; --ds-sx: 39; --ds-sy: 33.5; --ds-sw: 745.5; --ds-sh: 1618.5; --ds-r: 114; --dsf-frame: url('../../mockup-frames/phone.png'); }
.dsf--laptop { --ds-fw: 2628; --ds-fh: 1609; --ds-sx: 241.55; --ds-sy: 39.5; --ds-sw: 2144.91; --ds-sh: 1387; --ds-r: 24.5; --dsf-frame: url('../../mockup-frames/laptop.png'); }
.dsf--laptop > img { object-position: 50% 100%; }

