/* ========================================================================
   GagaMuller Learning — 08-deck-canvas.css
   The free-placement surface of the slide deck: the fixed canvas, the block
   frames, their handles, the snap guides and the empty-slide prompt.

   Its own sheet because it is one whole concern and a big one. It comes after
   07-editor.css so it can lean on the editor tokens and override the stage.
   ======================================================================== */

/* ---- the stage IS the learner's frame ----
   WHAT THE AUTHOR SEES IS WHAT THE LEARNER SEES. The learner reads a slide inside
   .pl-body: a full-height, scrollable frame that CENTRES a short slide and lets a
   long one scroll, with the content sized to its density (see 05-courses.css). The
   stage was a fixed 16:9 box that clipped overflow and packed everything to the
   top - so "Module 1" sat stranded at the top edge and a full slide was cut off,
   neither of which is how the course actually reads.

   The stage now frames the slide the SAME way .pl-body does: a tall canvas whose
   height tracks the viewport, the slide centred in it at the same 840px measure,
   the same padding, and a real scroll when the content outgrows the frame. The
   density rules in 05-courses.css then do the rest, identically to the player, so
   the stage is an accurate preview rather than a squeezed thumbnail.

   --dk-slide-h is the frame height budget: what is left of the viewport once the
   dialog chrome, the deck toolbar and the strip are accounted for. */
/* ---- the full-screen editor: a true player shell ----
   The deck tab fills the whole dialog (see .mdl.full.dk-fulledit in
   06-blocks.css). The slide sits inside a real player frame - a header (.pl-top),
   a scrolling body (.pl-body) and a footer (.pl-foot) - the same three parts the
   learner reads, but carrying the editor's controls instead of the learner's
   navigation. So the author authors inside the exact chrome the course ships in.

   .dk is the full-height row: a slide-out drawer on the left and the player
   frame filling the rest. --dk-slide-h is no longer a pixel budget here; the
   frame simply takes the height it is given. */
.dk-stage{--dk-slide-h:min(74vh, 740px);}
/* .dk is a flex child of #edPane (which dk-fulledit makes display:flex). Without
   flex:1 it shrinks to its content and leaves dead space on the right - the slide
   not reaching the edge of the screen. flex:1 + width:100% makes it fill the
   dialog edge to edge. */
.mdl.dk-fulledit .dk{flex:1; width:100%; height:100%; min-height:0; display:flex;
  gap:0; align-items:stretch; grid-template-columns:none; position:relative;}
.mdl.dk-fulledit .dk-main{flex:1; min-width:0; min-height:0; display:flex;
  flex-direction:column; padding:0; position:relative;}
.mdl.dk-fulledit .dk-stage{flex:1; min-height:0; display:flex; padding:0;
  background:var(--bg); border-radius:0;}
.mdl.dk-fulledit .dk-page{flex:1; min-width:0; max-width:none; display:flex;
  flex-direction:column;}
.mdl.dk-fulledit .dk-player{flex:1; min-height:0; width:100%; border:0;
  border-radius:0; box-shadow:none;}

/* The player frame everywhere the deck appears (also the smaller settings-side
   preview, if it is ever shown): a column of header, body, footer. In full
   screen the overrides above take the height from the dialog; elsewhere it falls
   back to the frame budget. */
.dk-player{display:flex; flex-direction:column; width:100%;
  height:var(--dk-slide-h); min-height:0; overflow:hidden; background:var(--bg);}

/* ---- the top bar: the editor's controls in the course header ----
   .dk-top reuses the learner .pl-top, so it reads as the course header, but it
   carries working controls grouped in the centre where the learner reads
   "Module N of M".

   SLIMMER than the learner's header, deliberately: it holds more (the dwell
   field and the alignment segments), so the vertical padding is pulled in to
   8px and the controls kept compact, which keeps the bar about the height of
   the learner's and leaves the slide body the same share of the screen. The
   point of this view is that the slide reads at the size the learner gets, so
   the chrome must not grow at the slide's expense. */
.dk-top{gap:12px; padding-top:8px; padding-bottom:8px;}
.dk-top .dk-top-where{display:flex; flex-direction:column; min-width:120px; max-width:30ch;}
.dk-top .dk-top-ctl{display:flex; align-items:center; gap:12px; flex-wrap:wrap;
  justify-content:center; flex:1;}
.dk-top .dk-top-slide{display:flex; align-items:center; gap:7px; flex:none;}
.dk-top .dk-ctl{display:flex; align-items:center; gap:6px;}
.dk-top .dk-ctl-l{font:700 9px var(--mono); letter-spacing:0.5px; color:var(--muted);
  text-transform:uppercase; white-space:nowrap;}
.dk-top .seg.dk-seg{padding:2px;}
.dk-top .seg.dk-seg button{padding:3px 8px; font-size:11.5px;}
/* The slide-level action buttons sit compact so the bar stays slim. */
.dk-top .dk-top-slide .btn.sm{padding:5px 11px;}

/* ---- the bottom bar: the course actions in the course footer ----
   .dk-foot reuses the learner .pl-foot, so it reads as the course footer, but it
   carries the editor's decisions - settings, preview, discard, close, save,
   publish - where the learner reads Back / Continue. Slimmed to match the slim
   header, so the slide between them keeps the learner's proportions. */
.dk-foot{gap:8px; padding-top:9px; padding-bottom:9px;}
.dk-foot .btn{padding:7px 14px;}

/* ---- the slide-out slide-list drawer ----
   The module tree, Add slide and Add module live in a drawer that slides in from
   the left, opened by an arrow on the edge.

   IT OVERLAYS THE SLIDE, it does not push it. The drawer is absolutely
   positioned over the left edge of the deck rather than being a flex column, so
   the slide below keeps the full width at all times - opening or closing the
   drawer never reflows the slide. Closed, it is slid fully off to the left and
   is inert; open, it slides in and sits on top, with a soft shadow so it reads
   as a layer above the stage. */
.dk-drawer{position:absolute; left:0; top:0; bottom:0; z-index:35; width:248px;
  background:var(--surface); border-right:1px solid var(--line);
  box-shadow:6px 0 24px rgba(0,0,0,.18);
  transform:translateX(-100%); transition:transform .22s cubic-bezier(.22,1,.36,1);
  overflow:hidden;}
.dk.strip-open .dk-drawer{transform:translateX(0);}
/* Closed, nothing in the drawer is clickable or focusable - it is off screen
   and must not steal a tab stop or a stray click from the slide beneath it. */
.dk:not(.strip-open) .dk-drawer{pointer-events:none; visibility:hidden;}
.dk.strip-open .dk-drawer{pointer-events:auto; visibility:visible;}
/* The strip inside the drawer is always the vertical module tree, overriding the
   narrow-window rule in 07-editor.css that lays it out horizontally for the old
   fixed-column layout - that rule does not apply to a slide-out drawer. */
.dk-drawer .dk-strip{width:248px; max-height:none; height:100%; box-sizing:border-box;
  border-right:0; border-bottom:0; flex-direction:column; overflow-x:hidden;
  overflow-y:auto; padding:14px 14px 16px; margin-bottom:0;}
.dk-drawer .dk-slides{flex-direction:column; border-left:2px solid var(--line);
  border-top:0; margin-left:8px; padding-left:4px; padding-top:0;}
.dk-drawer .dk-mod-group{min-width:0;}
.dk-drawer .dk-thumb{width:100%;}
/* The edge arrow. Always reachable at the left, it rotates to point the way the
   drawer will go. It sits above the stage so it is never covered. */
.dk-strip-toggle{position:absolute; left:0; top:50%; transform:translateY(-50%);
  z-index:40; display:grid; place-items:center; width:26px; height:52px;
  border:1px solid var(--line); border-left:0; border-radius:0 10px 10px 0;
  background:var(--surface); color:var(--muted); cursor:pointer;
  box-shadow:var(--shadow-s); transition:left .22s cubic-bezier(.22,1,.36,1),
    background .12s ease, color .12s ease;}
.dk-strip-toggle:hover{background:var(--brand-soft); color:var(--brand);}
.dk-strip-toggle svg{transition:transform .22s ease;}
.dk.strip-open .dk-strip-toggle{left:248px;}
.dk.strip-open .dk-strip-toggle svg{transform:rotate(180deg);}

/* Switching slide fades the stage gently rather than lifting the whole pane, so
   stepping through a deck feels like turning pages, not reloading the screen. */
@keyframes dkStageIn{from{opacity:0; transform:translate3d(10px,0,0);} to{opacity:1; transform:none;}}
.dk-stage.dk-turn{animation:dkStageIn .22s cubic-bezier(.22,.61,.36,1) both;}
/* The canvas is .pl-body now (it carries both classes), so it inherits the
   learner body's flex/scroll/padding directly and only needs the stage-specific
   bits: it is the FLEX CHILD of .dk-player that takes the space left between the
   header and footer, so flex:1 and min-height:0 rather than a fixed height. The
   rest - centring a short slide, scrolling a long one - comes from the shared
   .pl-body rule in 05-courses.css, which is the whole point of sharing the
   class: the stage body and the learner body cannot drift. */
.dk-stage .dk-canvas{position:relative; flex:1; min-height:0;
  overflow-x:hidden; background:var(--bg); touch-action:pan-y;}
.dk-stage .dk-canvas::-webkit-scrollbar{width:10px;}
.dk-stage .dk-canvas::-webkit-scrollbar-thumb{background:var(--fill-2);
  border-radius:99px; border:2px solid var(--bg);}
.dk-stage .dk-canvas::-webkit-scrollbar-track{background:transparent;}
/* The slide inside is sized and aligned entirely by 05-courses.css - the same
   rules the learner's slide obeys - so nothing about width, measure or vertical
   rhythm is re-specified here. A dk-free slide keeps its own genuine 16:9 canvas
   (05-courses.css), a real shape in the player too, so it is left untouched. */
.dk-page{width:100%; max-width:1040px; display:flex; justify-content:center;}
.dk-stage .dk-page{flex:1; min-width:0;}

/* ---- a block frame on the canvas ----
   Every block is wrapped in a .dk-item. It is quiet until hovered or selected,
   so an unselected slide reads as a slide. The selection frame is SQUARE - sharp
   corners, a crisp two-pixel outline - so it reads as a design tool's bounding
   box rather than a soft card. `outline` not `box-shadow`, so it is never clipped
   by the slide even when the block is flush to an edge. */
/* A FLOWING BLOCK HUGS ITS CONTENT, and that one missing rule was two faults.

   .dk-item is a block element, so with no width it filled the slide column. A
   three-word heading and a six-step list were both as wide as the slide, the
   hover and selection outline sat on a rectangle far bigger than the thing it
   was outlining, and the first drag then pulled the block in to the pickup
   width - which is what "it goes across the whole screen, then minimises when
   you press it" was describing.

   fit-content sizes the frame to what is in it. max-width keeps a long
   paragraph inside the column. A PLACED block is absolutely positioned and
   carries its own width, so it is untouched by this. */
.dk-item{position:relative; box-sizing:border-box;
  width:fit-content; max-width:100%;}
.dk-item.placed{width:auto;}
/* EXCEPT THE TYPES THAT ARE A FULL-WIDTH LAYOUT, which fit-content does not
   measure - it RESHAPES them.

   These were exempt, the exemption was removed on the theory that every block
   should hug, and that was wrong. A stat row is a three-column grid: asked for
   its fit-content width it collapses to one narrow column and grows tall -
   measured on the ISO course, a stat row went to 269x365 where the real block
   is 1035 wide and 120 tall. The frame then sits on a shape the block never
   had, which is the "boxes still don't match the item" report.

   fit-content is only a measurement when the content's width is content-driven.
   For a grid or a column pair it is an instruction, so those keep their real
   width and the frame wraps THAT. Nothing resizes them on pickup any more -
   that was DK_PICKUP_W, and it is gone - so the two can coexist now in a way
   they could not when the exemption was first dropped. */
/* A video is here for a sharper reason than the rest: its frame holds an
   absolutely-positioned iframe, so fit-content finds no width and it hugs nothing. */
.dk-item:not(.placed):has(.vid),
.dk-item:not(.placed):has(.dolist),
.dk-item:not(.placed):has(.grid),
.dk-item:not(.placed):has(.stepset),
.dk-item:not(.placed):has(.dk-stats),
.dk-item:not(.placed):has(.dk-flip-editor),
.dk-item:not(.placed):has(.dk-flip-pair),
.dk-item:not(.placed):has(.scen),
.dk-item:not(.placed):has(.dk-mail-preview),
.dk-item:not(.placed):has(.dk-rich){width:100%;}
/* THE WAY IN, to pair with .dk-leave's way out (07-editor.css).

   A block added from the palette appeared at full opacity the instant the stage
   rebuilt: .blk-new only glowed the border, so the block itself arrived hard
   while its outline faded politely. It rises and fades now, in the vocabulary
   dkItemOut leaves by, so adding and removing read as one pair of movements.

   On the ITEM, not the stage - an add refreshes the stage with turn:false
   precisely so the whole slide does not re-enter around one new block.

   It lives here rather than beside dkItemOut because 07-editor.css is at its
   500-line ceiling, and because every other .dk-item rule is in this file. */
@keyframes dkItemIn{
  from{opacity:0; transform:translate3d(0,6px,0) scale(.98);}
  to  {opacity:1; transform:none;}}
.dk-item.blk-new{animation:dkItemIn .2s cubic-bezier(.22,1,.36,1) both;}
.dk-item:hover{outline:1px solid var(--line); outline-offset:-1px;}
.dk-item.sel{outline:2px solid var(--brand); outline-offset:-1px; z-index:3;}
.dk-item.dk-drag{outline:2px solid var(--brand); outline-offset:-1px; z-index:6;}
/* The content is ZOOMED by --dk-scale so text, spacing and everything else grow
   and shrink with the box the way an image does - not just the ones that happen
   to use relative units. It is laid out at 1/scale of the frame width and then
   transform-scaled back up, so after scaling it fills the frame exactly.

   The zoom, the fixed height and the clip are a PLACED block's business only -
   an absolutely-positioned box has a height to fill and content that must not
   spill past its outline. A FLOWING block (one never dragged out of the normal
   column) has no fixed box: its frame must hug the real content so the selection
   and hover outline sits exactly on what the block is, not on an oversized or
   clipped rectangle. So a flowing item keeps auto height, no zoom and no clip -
   the same treatment the module heading already got - and only .placed takes the
   scaled, fixed, centred box. */
.dk-item-in{width:100%; height:auto; transform:none; overflow:visible;}
.dk-item.placed .dk-item-in{width:calc(100% / var(--dk-scale, 1));
  height:calc(100% / var(--dk-scale, 1)); transform:scale(var(--dk-scale, 1));
  transform-origin:top left; overflow:hidden; display:flex; flex-direction:column;
  justify-content:center;}

/* ---- the module heading as a movable item ----
   The kicker + title are wrapped in a .dk-item so they can be picked up and
   placed like any block (data-posbi="head", geometry stored on m.headPos). While
   the heading is still flowing - never moved - the frame must behave like the
   plain heading it replaces: full width, auto height, no clipping, and the
   centred text rules in 05-courses.css left to do their work. Only once it is
   .placed does it take on the absolute box, the fixed height and the zoom every
   other placed item gets. */
/* IT HUGS LIKE EVERYTHING ELSE. width:100% was here to keep a never-moved
   heading looking like the plain heading it replaced, but it also meant the
   selection frame round "Module 1" ran the whole width of the slide - a box
   with four words in it and six hundred pixels of nothing. align-self keeps it
   centred, which is what the full width was really buying. */
.dk-item.dk-head:not(.placed){align-self:center;}
.slide.pl-al-l .dk-item.dk-head:not(.placed){align-self:flex-start;}
.slide.pl-al-r .dk-item.dk-head:not(.placed){align-self:flex-end;}
/* The centring rules in 05-courses.css use the direct-child combinator
   (.slide > h2 / .slide > .kicker), which the wrapper breaks. Restate them for
   the heading inside the frame so a heading that has NOT been moved reads exactly
   as it did before - centred, held to a sensible measure. Alignment overrides
   (pl-al-*) still win because they are more specific and come later. */
.dk-item.dk-head .kicker, .dk-item.dk-head h2{text-align:center;}
.dk-item.dk-head h2{margin-left:auto; margin-right:auto; max-width:34ch;}
.slide.pl-al-l .dk-head .kicker, .slide.pl-al-l .dk-head h2{text-align:left;}
.slide.pl-al-r .dk-head .kicker, .slide.pl-al-r .dk-head h2{text-align:right;}
.slide.pl-al-l .dk-head h2{margin-left:0; margin-right:auto;}
.slide.pl-al-r .dk-head h2{margin-left:auto; margin-right:0;}

/* ---- the move grip ----
   INSIDE the top-left corner, not outside it, so the slide's overflow clip never
   swallows it when the block is at an edge - the bug that made the drag button
   feel dead. It sits over the content but only shows when the block is live. */
.dk-move{position:absolute; top:2px; left:2px; z-index:8; display:grid;
  place-items:center; width:24px; height:24px; border-radius:7px;
  background:var(--brand); border:1px solid var(--surface); color:#fff;
  cursor:grab; box-shadow:var(--shadow-s); opacity:0; transition:opacity .12s ease;
  touch-action:none;}
.dk-move:active{cursor:grabbing;}
.dk-item:hover > .dk-move, .dk-item.sel > .dk-move, .dk-item.dk-drag > .dk-move{opacity:1;}

/* ---- the eight resize handles ----
   Square, and INSET so they straddle the item's own edge and are never clipped
   by the slide, however tight to the edge the block is. Drawn only when the item
   is selected. Corners scale the content; edges change one dimension. */
.dk-h{position:absolute; z-index:8; width:11px; height:11px;
  background:var(--brand); border:1.5px solid var(--surface); box-shadow:var(--shadow-s);
  opacity:0; transition:opacity .12s ease; touch-action:none;}
.dk-item.sel > .dk-h, .dk-item.dk-drag > .dk-h{opacity:1;}
.dk-h-n{top:0; left:50%; transform:translate(-50%,-50%); cursor:ns-resize;}
.dk-h-s{bottom:0; left:50%; transform:translate(-50%,50%); cursor:ns-resize;}
.dk-h-e{right:0; top:50%; transform:translate(50%,-50%); cursor:ew-resize;}
.dk-h-w{left:0; top:50%; transform:translate(-50%,-50%); cursor:ew-resize;}
.dk-h-ne{top:0; right:0; transform:translate(50%,-50%); cursor:nesw-resize;}
.dk-h-sw{bottom:0; left:0; transform:translate(-50%,50%); cursor:nesw-resize;}
.dk-h-nw{top:0; left:0; transform:translate(-50%,-50%); cursor:nwse-resize;}
.dk-h-se{bottom:0; right:0; transform:translate(50%,50%); cursor:nwse-resize;}

/* ---- during a drag ----
   Nothing underneath may select or catch the pointer, or the caret lands
   mid-move and the browser starts a text selection. The dragged item stays live
   so its own handles keep receiving the capture. */
.slide.dk-dragging, .slide.dk-dragging *{user-select:none; -webkit-user-select:none;}
.slide.dk-dragging .dk-ed, .slide.dk-dragging .dk-rich{pointer-events:none;}
.slide.dk-dragging .dk-item.dk-drag{pointer-events:auto;}

/* ---- the drag placeholder ----
   Reserves the exact space a flowing block occupied while it is lifted out to be
   dragged, so the rest of the slide - the "Module N of M" kicker, the heading,
   every other block - does not reflow into the gap. Invisible and inert; sized in
   pixels by deck-canvas.js and removed on release. flex:none so the flex column
   honours its height instead of collapsing it. */
.dk-drag-ghost{flex:none; pointer-events:none; visibility:hidden;}

/* ---- the snap guides ----
   A crisp brand line down or across the slide where a block has snapped to a
   centre or an edge, removed the instant it no longer applies. */
.dk-guide{position:absolute; z-index:4; pointer-events:none; background:var(--brand);
  opacity:.7;}
.dk-guide-v{top:0; bottom:0; width:1px; transform:translateX(-0.5px);}
.dk-guide-h{left:0; right:0; height:1px; transform:translateY(-0.5px);}

/* ---- an empty slide ----
   A quiet prompt, not an input box, until a block is added. */
.dk-empty{display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:6px; text-align:center; padding:40px 20px; margin:auto 0; color:var(--muted);
  border:1px dashed var(--line); border-radius:14px; background:var(--fill);}
.dk-empty h4{font:700 14px var(--display); color:var(--ink); margin:0;}
.dk-empty .mini{margin:0;}

/* ---- on-block actions ----
   A small toolbar on the selected block for the things that are not a drag: edit
   (rich blocks) and delete. Anchored to the top-right corner, inside the frame so
   the slide clip never eats it. */
.dk-item-bar{position:absolute; top:2px; right:2px; z-index:8; display:none; gap:4px;}
.dk-item.sel > .dk-item-bar, .dk-item:hover > .dk-item-bar{display:flex;}
.dk-ib{display:grid; place-items:center; width:24px; height:24px; border-radius:7px;
  background:var(--surface); border:1px solid var(--line); color:var(--ink);
  cursor:pointer; box-shadow:var(--shadow-s);}
.dk-ib:hover{background:var(--brand-soft); border-color:var(--brand); color:var(--brand);}
.dk-ib.del:hover{background:var(--bad-soft, rgba(220,38,38,.12)); border-color:var(--bad); color:var(--bad);}

/* ---- block fields live inside the slide item ----
   Rich blocks edit in place: the author works on the slide itself, never in an
   inspector tab beside or below it. The existing block form fields are kept,
   but contained in a quiet surface that sizes with the block's content. */
.dk-rich{width:100%; min-width:0;}
.dk-inline-editor{display:grid; gap:12px; width:100%; min-width:0; box-sizing:border-box;
  padding:14px; border:1px solid var(--line); border-radius:10px;
  background:var(--surface); color:var(--ink);}
.dk-inline-editor .fld{min-width:0; margin:0;}
.dk-inline-editor textarea{width:100%; resize:vertical;}
.dk-inline-editor .rowset{display:grid; gap:9px;}
/* A TRANSPARENT WRAPPER. It carried max-width:760px and its own margin - a
   second set of sizing rules beside the player's, so the learner's video was
   840 wide and the author's 756 from the same data. The player's own rule
   (.slide .dk-item-in .vid, 05-courses.css) sizes it now. */
.dk-video-preview{display:grid; gap:0; width:100%; min-width:0;}
.dk-video-title,.dk-video-cap{min-width:2ch;}
.dk-video-title:empty::before,.dk-video-cap:empty::before{content:attr(data-ph); color:var(--muted); opacity:.65;}

/* Reveal cards retain the learner's actual flip animation and front/back
   styling. Their compact author controls sit above, outside the card itself. */
.dk-flip-editor{display:grid; grid-template-columns:repeat(auto-fit,minmax(215px,1fr));
  gap:12px; margin:20px 0;}
.dk-flip-card-editor{display:grid; grid-template-rows:auto 196px; gap:6px; min-width:0;}
.dk-flip-toolbar{display:flex; align-items:center; justify-content:flex-end; gap:7px;}
.dk-flip-preview{height:196px; cursor:default;}
.dk-flip-preview .flip-f,.dk-flip-preview .flip-b{cursor:default;}
.dk-flip-preview .dk-ed-item{min-width:2ch;}
.dk-flip-preview .dk-flip-toggle{pointer-events:auto; cursor:pointer;}
.dk-flip-icon-row{display:flex; align-items:center; gap:7px; margin:0;
  font:600 10px var(--mono); color:var(--muted); text-transform:uppercase;}
.dk-flip-icon-row select{width:auto; min-width:100px; padding:4px 7px; font-size:11px;}
.dk-flip-del{display:grid; place-items:center; flex:none;
  width:25px; height:25px; border:1px solid var(--line); border-radius:7px;
  background:var(--surface); color:var(--muted); box-shadow:var(--shadow-s);}
.dk-flip-del:hover{color:var(--bad); background:var(--bad-soft);}
.dk-flip-del svg{width:13px; height:13px;}
.dk-inline-add{grid-column:1 / -1; justify-self:start; align-self:start; display:inline-flex; align-items:center; gap:6px; padding:6px 10px;
  border:1px dashed var(--line); border-radius:8px; color:var(--brand); font-size:12px;}
.dk-inline-add:hover{border-color:var(--brand); background:var(--brand-soft);}
.dk-inline-add svg{width:13px; height:13px;}

/* Numbered steps use the same learner cards, typography and badge hierarchy;
   the plus inserts a new step between these cards. */
.dk-steps-editor{gap:0;}
.dk-step-editor-row{display:grid; justify-items:start;}
.dk-step-editor-row .stp{position:relative; width:100%; box-sizing:border-box;}
.dk-step-editor-row .stp-b h4,.dk-step-editor-row .stp-x{min-height:1.4em;}
.dk-step-editor-row .dk-step-del{position:absolute; top:5px; right:5px; opacity:0;
  transition:opacity .12s ease;}
.dk-step-editor-row:hover .dk-step-del,.dk-step-editor-row:focus-within .dk-step-del{opacity:1;}
.dk-step-add{position:relative; z-index:2; display:grid; place-items:center; width:22px; height:22px;
  margin:-4px 0 2px 16px; border:1px solid var(--line); border-radius:50%;
  color:var(--brand); background:var(--surface); box-shadow:var(--shadow-s);}
.dk-step-add:hover{border-color:var(--brand); background:var(--brand-soft);}
.dk-step-add svg{width:12px; height:12px;}
.dk-step-editor-row .stp.d1{margin-left:30px; width:calc(100% - 30px);}
.dk-step-editor-row .stp.d2{margin-left:60px; width:calc(100% - 60px);}
.dk-step-editor-row .stp.d3{margin-left:90px; width:calc(100% - 90px);}
.dk-step-editor-row .stp.d1 + .dk-step-add{margin-left:46px;}
.dk-step-editor-row .stp.d2 + .dk-step-add{margin-left:76px;}
.dk-step-editor-row .stp.d3 + .dk-step-add{margin-left:106px;}
.dk-step-del{display:grid; place-items:center; width:26px; height:26px; flex:none;
  border-radius:7px; color:var(--muted);}
.dk-step-del:hover{background:var(--bad-soft); color:var(--bad);}
.dk-step-del svg{width:13px; height:13px;}
.dk-stat{position:relative; min-width:0;}
.dk-inline-remove{position:absolute; top:5px; right:5px; display:grid; place-items:center;
  width:23px; height:23px; border-radius:6px; color:var(--muted); opacity:0;}
.dk-stat:hover .dk-inline-remove,.dk-scen-edit .scen-o:hover .dk-inline-remove,
.dk-stat:focus-within .dk-inline-remove,.dk-scen-edit .scen-o:focus-within .dk-inline-remove{opacity:1;}
.dk-inline-remove:hover{background:var(--bad-soft); color:var(--bad);}
.dk-inline-remove svg{width:13px; height:13px;}
.dk-dolist > div{position:relative; min-width:0;}
.dk-dolist li{position:relative; padding-right:25px;}
.dk-ed-list{min-width:2ch; outline:none; border-radius:4px; cursor:text;}
.dk-ed-list:empty::before{content:attr(data-ph); color:var(--muted); opacity:.65;}
.dk-list-del{position:absolute; top:0; right:0; display:grid; place-items:center;
  width:20px; height:20px; border-radius:5px; color:var(--muted); opacity:0;}
.dk-dolist li:hover .dk-list-del,.dk-dolist li:focus-within .dk-list-del{opacity:1;}
.dk-list-del:hover{color:var(--bad); background:var(--bad-soft);}
.dk-list-del svg{width:12px; height:12px;}
.dk-dolist .dk-inline-add{margin-top:8px;}
.dk-scenario-q{min-height:1.4em;}
.dk-scen-edit .scen-o{position:relative; cursor:default;}
.dk-scen-edit .dk-scen-copy{flex:1; min-width:0;}
.dk-scen-edit .fb{display:none;}
.dk-scen-edit .scen-o:hover .fb,.dk-scen-edit .scen-o:focus-within .fb{display:block;}
.dk-scen-correct{display:flex; align-items:center; gap:5px; flex:none;
  font:600 9px var(--mono); color:var(--muted); text-transform:uppercase;}
.dk-scen-correct input{accent-color:var(--ok);}
.dk-file-editor{display:grid; gap:0; margin:16px 0;}
.dk-file-editor .fileblk{margin:0;}
  padding:9px 11px; border:1px solid var(--line); border-top:0;
  border-radius:0 0 var(--radius-s) var(--radius-s); background:var(--surface);}
/* No margin of its own: .mail already carries 20px 0 from the player's sheet,
   and 16px 0 8px here spaced the block differently for the author. */
.dk-mail-preview{min-width:0;}
.dk-mail-fields{margin:0 0 12px; border-top:1px solid var(--line); color:var(--muted);}
.dk-mail-fields summary{padding:9px 2px; cursor:pointer; font:600 11px var(--mono);}
.dk-mail-fields[open] summary{color:var(--brand);}
.dk-mail-fields .dk-inline-editor{padding:12px; border-radius:8px;}
@media (max-width:620px){
  .dk-flip-pair{grid-template-columns:1fr;}
    }
.dk-hotspot-list{display:grid; gap:8px; margin-top:10px;}
.dk-hotspot-edit{display:grid; gap:7px; padding:9px; border:1px solid var(--line);
  border-radius:9px; background:var(--raised);}
.dk-hotspot-fields{display:flex; gap:7px; min-width:0;}
.dk-hotspot-fields input{min-width:0; flex:1;}
.dk-hotspot-fields input:first-child{max-width:70px;}
.dk-hotspot-edit textarea{width:100%; box-sizing:border-box;}

/* ---- the add button and its palette ----
   A floating action button in the TOP-RIGHT of the slide area, sitting ON the
   slide just inside the course header - where an author's eye lands first and
   where the hand is already heading to lay the slide out top-down. It lives in
   .dk-fab under .dk-main (not inside the scrolling canvas), so it never scrolls
   away with the slide. The palette opens DOWNWARD from it, because the button
   sits near the top.

   It floats OVER the slide rather than in a bar, so adding a block happens on
   the slide itself. top clears the ~52px course header with a little breathing
   room below it; right matches the header's own gutter so the bubble lines up
   with the chrome above it. */
.dk-fab{position:absolute; right:18px; top:86px; z-index:40;}
.dk-fab-b{display:grid; place-items:center; width:46px; height:46px; border-radius:50%;
  background:var(--brand); color:#fff; border:none; cursor:pointer;
  box-shadow:0 6px 20px color-mix(in srgb, var(--brand) 45%, transparent), var(--shadow-l);
  transition:transform .14s cubic-bezier(.22,1,.36,1), box-shadow .14s ease;}
.dk-fab-b:hover{transform:scale(1.08);
  box-shadow:0 8px 26px color-mix(in srgb, var(--brand) 55%, transparent), var(--shadow-l);}
.dk-fab.open .dk-fab-b{transform:rotate(45deg);}
/* The palette drops from under the button. A soft entrance so it reads as a
   layer settling onto the slide rather than a hard pop. */
.dk-palette{position:absolute; right:0; top:56px; width:264px; max-height:min(64vh,460px);
  overflow-y:auto; padding:7px; display:none; grid-template-columns:1fr 1fr; gap:4px;
  background:var(--surface); border:1px solid var(--line); border-radius:14px;
  box-shadow:var(--shadow-l); transform-origin:top right;}
.dk-fab.open .dk-palette{display:grid; animation:dkPalIn .16s cubic-bezier(.22,1,.36,1) both;}
@keyframes dkPalIn{from{opacity:0; transform:translate3d(0,-6px,0) scale(.97);}
  to{opacity:1; transform:none;}}
.dk-pal-b{display:flex; align-items:center; gap:6px; padding:8px 9px; border-radius:8px;
  border:1px solid transparent; background:transparent; cursor:pointer; text-align:left;
  font:600 11.5px var(--display); color:var(--ink);}
.dk-pal-b:hover{background:var(--brand-soft); border-color:var(--brand);}
.dk-pal-b svg{flex:none; color:var(--brand);}
.dk-main{position:relative;}

/* ---- time-required control, now in the course header ---- */
.dk-dwell{gap:6px;}
.dk-dwell input{width:52px; padding:3px 6px; border:1px solid var(--line);
  border-radius:7px; background:var(--surface); font:600 12px var(--mono); color:var(--ink);}
.dk-dwell input:focus{outline:none; border-color:var(--brand);}
.dk-dwell .mini{white-space:nowrap;}
