/* ========================================================================
   GagaMuller Learning — 07-editor.css
   The content editor, drag-to-reorder, the assumed-identity banner and the odds and ends.

   Loaded by index.html as sheet 7 of 7. The order is the cascade: a
   later sheet may override an earlier one, so it must not be changed.
   ======================================================================== */
  /* ---------- editor motion --------------------------------------------
     Two movements, and only two, so the editor never looks busy:

       .pane-in  a navigation - a tab change or a different module - settles in
                 with the same quarter-second lift the main view transition
                 uses, so the editor feels like the rest of the application
                 rather than like a separate program;
       .blk-new  one block announcing itself, once. Used when a block is added,
                 duplicated, or dropped somewhere new. It is a highlight that
                 recedes, not a bounce: the border and the header carry it and
                 nothing moves, so the page does not reflow under the cursor.

     Everything else is a transition on named properties. The editor was full
     of `transition:.14s`, which is transition-property:all - it animates
     height, width and margin too, so a block growing a field crept into place
     over 140ms and a repaint could catch mid-crawl. Naming the properties is
     what makes the difference between smooth and soft. */
  @keyframes edPaneIn{from{opacity:0; transform:translate3d(0,4px,0);} to{opacity:1; transform:none;}}
  #edPane.pane-in{animation:edPaneIn .28s cubic-bezier(.22,.61,.36,1) both;}
  /* No 100% keyframe and no fill mode on purpose. The endpoint is synthesised
     from the element's own computed style, so the highlight recedes to whatever
     that block should look like - a plain border, or the brand one if it is the
     selected block - instead of a fill mode pinning one hard-coded colour on
     top of it once the animation has finished. */
  @keyframes edBlkNew{
    0%  {border-color:var(--brand-solid); box-shadow:0 0 0 3px var(--brand-soft);}
    55% {border-color:var(--brand-solid); box-shadow:0 0 0 3px var(--brand-soft);}}
  .blk.blk-new, .ed-mod.blk-new{animation:edBlkNew 1.1s cubic-bezier(.4,0,.2,1);}

  /* ---------- surgical deck transitions --------------------------------
     The blink the whole editor used to do was repaintPane() swapping the
     entire #edPane innerHTML on every edit - the strip, the stage and the
     inspector all torn down and rebuilt so a single new thumbnail cost a
     full-tree reflow. These are the animations for the replacement: instead
     of rebuilding everything, deck-wiring.js now inserts, removes or swaps
     ONLY the node the author touched and plays one of these on it. Nothing
     around the change moves that did not have to.

     Every one of these is a named transform/opacity animation, never `all`,
     for the same reason the note above gives: a repaint mid-crawl on width or
     height is what reads as a stutter. The prefers-reduced-motion rule in
     01-tokens.css switches the lot off, so this is motion for those who want
     it and instantaneous for those who do not.

     A thumbnail arriving: it grows from nothing into its row and settles, so
     the strip opens a space for it rather than the whole list jumping down. */
  @keyframes dkThumbIn{
    from{opacity:0; transform:translate3d(0,-6px,0) scale(.96); max-height:0; margin-bottom:-6px;}
    60% {opacity:1;}
    to  {opacity:1; transform:none; max-height:220px; margin-bottom:0;}}
  .dk-thumb.dk-enter{animation:dkThumbIn .26s cubic-bezier(.22,.61,.36,1) both; overflow:hidden;}
  /* A thumbnail leaving: it collapses its own height and fades, so the slides
     under it slide up into the gap instead of the strip blinking to a shorter
     list. animationend in deck-wiring.js removes the node once this finishes. */
  @keyframes dkThumbOut{
    from{opacity:1; transform:none; max-height:220px;}
    to  {opacity:0; transform:translate3d(0,-4px,0) scale(.96); max-height:0;
         margin-bottom:-6px; padding-top:0; padding-bottom:0;}}
  .dk-thumb.dk-leave{animation:dkThumbOut .22s cubic-bezier(.4,0,1,1) both;
    overflow:hidden; pointer-events:none;}

  /* A whole module group arriving or leaving in the tree, the same movement a
     slide gets but for the heading-plus-slides unit. */
  @keyframes dkGroupIn{
    from{opacity:0; transform:translate3d(0,-8px,0);}
    to  {opacity:1; transform:none;}}
  .dk-mod-group.dk-enter{animation:dkGroupIn .28s cubic-bezier(.22,.61,.36,1) both;}
  @keyframes dkGroupOut{
    from{opacity:1; transform:none; max-height:600px;}
    to  {opacity:0; transform:translate3d(0,-6px,0); max-height:0; margin-bottom:-14px;}}
  .dk-mod-group.dk-leave{animation:dkGroupOut .24s cubic-bezier(.4,0,1,1) both;
    overflow:hidden; pointer-events:none;}

  /* A block leaving the stage: it recedes on the spot - fades and shrinks a
     little toward its own centre - so the block the author pressed delete on
     visibly goes rather than the slide flickering to one block fewer.
     animationend removes it and the model is reconciled behind the fade. */
  @keyframes dkItemOut{
    from{opacity:1; transform:scale(1);}
    to  {opacity:0; transform:scale(.9);}}
  .dk-item.dk-leave{animation:dkItemOut .18s cubic-bezier(.4,0,1,1) both;
    pointer-events:none;}


  /* ---------- editor ---------- */
  .ed{display:grid; grid-template-columns:250px 1fr; gap:0; min-height:520px;}
  .ed-side{border-right:1px solid var(--line); padding:0 16px 0 0; display:flex; flex-direction:column; gap:5px;}
  .ed-side .lbl{font:700 10px var(--mono); letter-spacing:0.50px; color:var(--muted); padding:12px 4px 6px;}
  .ed-mod{display:flex; align-items:center; gap:9px; padding:9px 11px; border-radius:10px; width:100%;
    text-align:left; font:600 12.8px var(--display); color:var(--muted); border:1px solid transparent;
    transition:background .14s ease, color .14s ease, border-color .14s ease;}
  .ed-mod:hover{background:var(--fill); color:var(--ink);}
  .ed-mod.on{background:var(--brand-soft); color:var(--brand); border-color:color-mix(in srgb, var(--brand) 30%, transparent);}
  .ed-mod .n{font:700 10px var(--mono); width:17px; flex:none;}
  .ed-mod .gr{margin-left:auto; display:flex; gap:2px; opacity:0; transition:opacity .14s ease;}
  .ed-mod:hover .gr{opacity:1;}
  .ed-mod .gr button{width:20px; height:20px; border-radius:6px; display:grid; place-items:center; color:var(--muted);}
  .ed-mod .gr button:hover{background:var(--fill-2); color:var(--ink);}
  .ed-mod .gr svg{width:11px; height:11px; fill:none; stroke:currentColor; stroke-width:2;}
  .ed-mod .gr .del{width:18px; height:18px;}
  .ed-mod .gr .del:hover{color:var(--bad); background:var(--bad-soft);}
  .ed-mod{cursor:pointer; position:relative;} .ed-mod .dragh:active{cursor:grabbing;}
  .ed-main{padding:0 4px 0 20px; min-width:0;}
  .blk{border:1px solid var(--line); border-radius:var(--radius-s); background:var(--raised); margin-bottom:11px;
    overflow:hidden; position:relative;}
  .blk-h{display:flex; align-items:center; gap:9px; padding:9px 12px; background:var(--fill); cursor:grab;}
  .blk-h .bt{font:700 10px var(--mono); letter-spacing:0.45px; color:var(--brand);
    background:var(--brand-soft); padding:2px 8px; border-radius:6px;}
  .blk-h .bs{font-size:12px; color:var(--muted); flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
  .blk-h button{width:22px; height:22px; border-radius:5px; display:grid; place-items:center; color:var(--muted);}
  .blk-h button:hover{background:var(--fill-2); color:var(--ink);}
  .blk-h svg{width:13px; height:13px; fill:none; stroke:currentColor; stroke-width:2.2; stroke-linecap:round;}
  /* The bin was the same weight as everything else on the bar, so it read as a
     primary action on a row you are usually editing, not deleting. */
  .blk-h .del{width:20px; height:20px;}
  .blk-h .del svg{width:11px; height:11px; stroke-width:2;}
  .blk-h .del:hover{color:var(--bad); background:var(--bad-soft);}

  /* Drag to reorder, click to select. */
  .blk-h{cursor:pointer;}
  .blk-h .dragh:active{cursor:grabbing;}
  /* Text inside a block selects normally now that only the handle drags. */
  .blk-b, .blk-h .bs{user-select:text;}
  .blk.sel{border-color:var(--brand);}
  .blk.sel > .blk-h{background:var(--brand-soft);}
  /* ---------- reordering ------------------------------------------------
     The old feedback was two things, and neither said where the block would
     land: the source dropped to 45% opacity, and whatever you hovered grew a
     line along its top edge - the SAME line whether you were going to end up
     above it or below it. And because the draggable element is the grip, the
     browser's drag image was the grip icon: a 14px handle floating across the
     screen with no sign of what was attached to it.

     Now the block itself is the drag image (setDragImage in wireReorder), it
     lifts out of the list rather than fading in place, and the drop line is
     drawn on the edge it will actually go to.

     The block that comes away under the cursor has to be SOLID, and getting
     that right is a matter of ordering rather than of styling. The browser
     rasterises the drag image from the LIVE element when the dragstart handler
     returns - so fading the element inside that handler, which is what adding
     .dragging first did, faded the picture as well as the original. The block
     appeared to dissolve rather than to be picked up.

     Two states now, and the order they are applied in is the whole fix:

       .lifting  is what is on the element while the snapshot is taken. Full
                 opacity, no filter, a raised shadow and a brand edge, so the
                 card following the pointer reads as held above the page;
       .dragging replaces it on the next frame, once the snapshot exists. It
                 marks the block's own place in the list while it is in flight.

     That second state used to hollow the block out - a dashed outline with its
     contents hidden - which read as the block having been deleted rather than
     picked up, and on a long drag you lost track of which one you were holding.
     It stays SOLID and fully readable now. The place it came from is shown by
     the border and the tinted header, not by taking the content away. */
  .blk.lifting, .ed-mod.lifting{
    opacity:1; filter:none; border-color:var(--brand);
    box-shadow:0 14px 30px rgba(16,10,40,.28), 0 2px 8px rgba(16,10,40,.18);}
  .blk.dragging, .ed-mod.dragging{
    opacity:1; border-color:var(--brand); background:var(--raised);
    box-shadow:inset 0 0 0 1px color-mix(in srgb, var(--brand) 30%, transparent);}
  .blk.dragging > .blk-h{background:var(--brand-soft);}
  /* Nothing inside the held block reacts to the pointer while it is in flight,
     so a field under the cursor cannot take focus mid-drag. */
  .blk.dragging > *, .ed-mod.dragging > *{visibility:visible; pointer-events:none;}
  /* While a drag is in progress the list stops offering hover affordances, so
     the only thing moving is the line. */
  .reordering .blk-h, .reordering .ed-mod{cursor:grabbing;}
  .reordering .blk:not(.dragging):not(.lifting):hover{border-color:var(--line);}
  /* The line. 3px on the edge it will land on, inset a little from both ends so
     it reads as an insertion point between two cards rather than as a border
     belonging to one of them. */
  .blk.drop-before::before, .blk.drop-after::after,
  .ed-mod.drop-before::before, .ed-mod.drop-after::after{
    content:''; position:absolute; left:6px; right:6px; height:3px; z-index:3;
    background:var(--brand-grad); border-radius:3px; pointer-events:none;
    box-shadow:0 0 0 1px var(--bg), 0 0 10px color-mix(in srgb, var(--brand) 65%, transparent);
    animation:dropIn .12s ease both;}
  .blk.drop-before::before, .ed-mod.drop-before::before{top:-7px;}
  .blk.drop-after::after,  .ed-mod.drop-after::after{bottom:-7px;}
  @keyframes dropIn{from{opacity:0; transform:scaleX(.9);} to{opacity:1; transform:none;}}

  /* ---------- the drag scroll arrows ------------------------------------
     Drawn over the top and bottom of the scrolling pane while a block is being
     held, so the auto-scroll edges are something you can see rather than
     something you have to discover. Painted over the body rather than inside
     the pane, because anything inside it scrolls away with the content.

     pointer-events:none throughout. These are a picture of a hot zone the
     dragover handler already reads; they are deliberately not drop targets. */
  .drag-scrollers{position:fixed; inset:0; z-index:340; pointer-events:none;}
  /* The element only exists for the duration of a drag, so there is no state
     class to hide it behind: it fades in on arrival, sits faint enough to read
     as "come here" rather than as a control that appeared by surprise, and
     brightens to brand while it is the one scrolling. */
  .ds{position:fixed; display:flex; align-items:center; gap:7px;
    padding:8px 14px; border-radius:999px; pointer-events:none;
    background:color-mix(in srgb, var(--surface) 88%, transparent);
    border:1px solid var(--line); box-shadow:var(--shadow);
    color:var(--muted); font:600 11.5px var(--display); white-space:nowrap;
    opacity:.62; transform:translateX(-50%) scale(1);
    transition:opacity .18s ease, transform .18s cubic-bezier(.22,1,.36,1),
               background .18s ease, color .18s ease, border-color .18s ease;}
  /* The entry state, removed on the next frame so the transition above runs.
     A keyframe animation cannot do this job: its forwards fill would pin the
     opacity and the highlight below would never take. */
  .ds.ds-enter{opacity:0; transform:translateX(-50%) scale(.94);}
  .ds.on{opacity:1; color:var(--brand); border-color:var(--brand-solid);
    background:var(--brand-soft); transform:translateX(-50%) scale(1.05);}
  .ds svg{width:14px; height:14px; fill:none; stroke:currentColor; stroke-width:2.4;
    stroke-linecap:round; stroke-linejoin:round;}
  .ds.on svg{animation:dsNudge 1s ease-in-out infinite;}
  .ds-up.on svg{animation-name:dsNudgeUp;}
  @keyframes dsNudge  {0%,100%{transform:translateY(0);} 50%{transform:translateY(2px);}}
  @keyframes dsNudgeUp{0%,100%{transform:translateY(0);} 50%{transform:translateY(-2px);}}
  .blk-b{padding:13px; display:grid; gap:11px; border-top:1px solid var(--line);}
  .blk.closed .blk-b{display:none;}
  .blk-add{display:flex; gap:7px; flex-wrap:wrap; padding:13px; border:1px dashed var(--line);
    border-radius:var(--radius-s); background:var(--fill); margin-top:6px;}
  .blk-add button{padding:6px 11px; border-radius:999px; border:1px solid var(--line); background:var(--surface);
    font:600 11.5px var(--display); color:var(--muted);
    transition:background .14s ease, color .14s ease, border-color .14s ease;}
  .blk-add button:hover{border-color:var(--brand-solid); color:var(--brand); background:var(--brand-soft);}
  .rowset{display:grid; gap:7px;}
  .rowset .ri{display:flex; gap:7px; align-items:flex-start;}
  .rowset .ri input, .rowset .ri textarea{flex:1;}
  .rowset .ri button{width:30px; height:34px; border-radius:9px; display:grid; place-items:center;
    color:var(--muted); border:1px solid var(--line); background:var(--surface); flex:none;}
  .rowset .ri button:hover{color:var(--bad); border-color:color-mix(in srgb, var(--bad) 45%, transparent);}
  .rowset .ri svg{width:13px; height:13px; fill:none; stroke:currentColor; stroke-width:2.2;}
  /* Remove-an-entry buttons, wherever they are.

     They used to be styled only through .rowset .ri - which the do/don't lists
     and the stat tiles happen to use, and which reveal cards, numbered steps
     and scenario options do not. Those three put the same button in a plain
     flex row, where it inherited the bare-button reset: no size, and an icon
     with no intrinsic dimensions, so it computed to ZERO pixels wide. It was
     in the DOM, it was "visible" to the browser, and it could not be clicked -
     which is why removing one reveal card was impossible and deleting the whole
     block and starting again was the only way out.

     Selected by the attribute rather than a class, so a new list type gets a
     working control without anybody remembering to add one. */
  .blk-b button[data-bdel], .blk-b button[data-bldel], .blk-b button[data-bodel],
  .blk-b button[data-qodel]{
    width:30px; height:34px; flex:none; border-radius:9px; display:grid; place-items:center;
    color:var(--muted); border:1px solid var(--line); background:var(--surface);}
  .blk-b button[data-bdel]:hover, .blk-b button[data-bldel]:hover,
  .blk-b button[data-bodel]:hover, .blk-b button[data-qodel]:hover{
    color:var(--bad); border-color:color-mix(in srgb, var(--bad) 45%, transparent);
    background:var(--bad-soft);}
  .blk-b button[data-bdel] svg, .blk-b button[data-bldel] svg,
  .blk-b button[data-bodel] svg, .blk-b button[data-qodel] svg{
    width:13px; height:13px; fill:none; stroke:currentColor; stroke-width:2.2;}
  .ans{display:flex; align-items:center; gap:9px; padding:8px 11px; border-radius:10px;
    border:1px solid var(--line); background:var(--surface);}
  .ans.correct{border-color:var(--live); background:var(--ok-soft);}
  .ans input[type=radio]{accent-color:var(--ok); width:15px; height:15px; flex:none; margin:0;}
  .ans input[type=text]{border:0; background:none; padding:2px 0;}
  .ans input[type=text]:focus{box-shadow:none;}
  .ed-dirty{font:600 11.5px var(--mono); color:var(--warn); display:inline-flex; align-items:center; gap:6px;}
  .ed-dirty::before{content:""; width:6px; height:6px; border-radius:50%; background:var(--warn);}
  .diffbox{font:500 12px var(--mono); background:var(--raised); border:1px solid var(--line);
    border-radius:var(--radius-s); padding:13px; max-height:210px; overflow:auto; line-height:1.7;}
  .diffbox .add{color:var(--ok);} .diffbox .del{color:var(--bad);} .diffbox .ctx{color:var(--muted);}

  /* ---------- misc ---------- */
  .empty{text-align:center; padding:56px 26px; color:var(--muted);}
  .empty svg{width:38px; height:38px; margin-bottom:13px; fill:none; stroke:currentColor; stroke-width:1.5;}
  .hr{height:1px; background:var(--line); margin:20px 0;}
  .mini{font-size:12.5px; color:var(--muted);}
  .row{display:flex; align-items:center; gap:11px;}
  /* A NAME COLLISION, not a style choice.

     `.wrap` is the page column - 1680px wide, 28px side gutters, 90px of
     bottom padding to clear the fixed footer. `.row.wrap` was written as a
     modifier meaning "this row is allowed to wrap", and a class selector does
     not care what the author meant: all seventeen wrapping rows in this
     application have been picking up the page column's padding and max-width.

     That is where the dead space under the storage card, the People filters and
     the deadline panels came from - 90px of bottom padding inside a card, on
     every one of them, plus 28px of phantom indent on the left.

     Undone here rather than by renaming the modifier in seventeen places,
     because a rename that misses one is a silent regression and this is not.
     `.row.wrap` is more specific than `.wrap`, so these four win. */
  .row.wrap{flex-wrap:wrap; max-width:none; margin:0; padding:0;}
  .col{display:flex; flex-direction:column; gap:11px;}
  .page-head{display:flex; align-items:flex-end; gap:16px; flex-wrap:wrap; margin-bottom:22px;}
  .page-head p{color:var(--muted); margin:6px 0 0; max-width:74ch; font-size:14px;}

  /* ---------- the slide deck -------------------------------------------
     Three columns, which is what a deck editor is: what there is, the one in
     hand, and its properties. Filmstrip, stage, inspector - the arrangement
     every tool of this kind uses, because it is the one that lets somebody keep
     their place while changing one thing.

     Each column scrolls on its own rather than the dialog scrolling as a whole.
     That is what stops the filmstrip leaving the screen while an author works
     down a long inspector, and it is why the strip carries [data-dragscroll] -
     see findScroller() in drag-to-reorder.js. */
  .dk{display:grid; grid-template-columns:220px minmax(0,1fr); min-height:0; gap:4px;}
  .dk-strip{border-right:1px solid var(--line); padding:2px 14px 12px 0; overflow-y:auto;
    max-height:80vh; display:flex; flex-direction:column; gap:14px; scrollbar-gutter:stable;}
  .dk-main{display:flex; flex-direction:column; min-width:0; padding:0 0 0 20px;}

  /* THE MODULE TREE. A module is a heading with its slides nested beneath it,
     and the module name is edited right here on that heading. */
  .dk-mod-group{display:flex; flex-direction:column; gap:6px;}
  .dk-mod-head{display:flex; align-items:center; gap:7px; position:sticky; top:0;
    background:var(--surface); z-index:1; padding:4px 0 2px;}
  .dk-mod-n{display:grid; place-items:center; width:18px; height:18px; border-radius:6px;
    background:var(--brand-soft); color:var(--brand); font:700 10px var(--mono); flex:none;}
  .dk-mod-name{flex:1; min-width:0; font:700 12.5px var(--display); color:var(--ink);
    background:transparent; border:1px solid transparent; border-radius:7px;
    padding:4px 7px; transition:background .12s ease, border-color .12s ease;}
  .dk-mod-name:hover{background:var(--fill);}
  .dk-mod-name:focus{outline:none; background:var(--surface); border-color:var(--brand);}
  /* Delete the whole module, tucked into the head and quiet until the group is
     hovered so it does not shout next to the name. */
  .dk-mod-del{flex:none; display:grid; place-items:center; width:24px; height:24px;
    border-radius:7px; border:1px solid transparent; background:transparent;
    color:var(--muted); cursor:pointer; opacity:0; transition:opacity .12s ease,
    background .12s ease, color .12s ease;}
  .dk-mod-group:hover .dk-mod-del, .dk-mod-del:focus-visible{opacity:1;}
  .dk-mod-del:hover{background:var(--bad-soft, rgba(220,38,38,.12)); color:var(--bad);}
  .dk-slides{display:flex; flex-direction:column; gap:6px; padding-left:4px;
    border-left:2px solid var(--line); margin-left:8px;}

  /* The plus that adds a slide (under a module) or a module (under the tree).
     A quiet dashed row so it reads as "space for one more" rather than as a
     control competing with the thumbnails. */
  .dk-add{display:flex; align-items:center; justify-content:center; gap:6px; width:100%;
    padding:7px 10px; font:600 11.5px var(--display); color:var(--muted);
    background:transparent; border:1px dashed var(--line); border-radius:10px;
    cursor:pointer; transition:background .12s ease, border-color .12s ease, color .12s ease;}
  .dk-add:hover{background:var(--brand-soft); border-color:var(--brand); color:var(--brand);}
  .dk-add:focus-visible{outline:2px solid var(--brand); outline-offset:2px;}
  .dk-add svg{opacity:.8;}
  .dk-add-mod{margin-top:2px; border-style:solid; font-weight:700; color:var(--ink);}
  .dk-add-mod:hover{color:var(--brand);}

  .dk-thumb{position:relative; display:flex; align-items:flex-start; gap:8px; width:100%;
    text-align:left; padding:5px; border-radius:11px; border:1px solid transparent;
    background:transparent; cursor:pointer; transition:background .14s ease, border-color .14s ease;}
  /* Delete this one slide, top-right of its thumbnail, quiet until hovered. */
  .dk-thumb-del{position:absolute; top:3px; right:3px; z-index:2; display:grid;
    place-items:center; width:22px; height:22px; border-radius:6px;
    border:1px solid var(--line); background:var(--surface); color:var(--muted);
    cursor:pointer; opacity:0; transition:opacity .12s ease, background .12s ease, color .12s ease;}
  .dk-thumb:hover .dk-thumb-del, .dk-thumb-del:focus-visible{opacity:1;}
  .dk-thumb-del:hover{background:var(--bad-soft, rgba(220,38,38,.12)); color:var(--bad); border-color:var(--bad);}
  /* It is a div with a button role, so the focus ring the browser would have
     drawn on a <button> has to be asked for. */
  .dk-thumb:focus-visible{outline:2px solid var(--brand); outline-offset:2px;}
  .dk-thumb:hover{background:var(--fill);}
  .dk-thumb.on{border-color:var(--brand); background:var(--brand-soft);}
  .dk-thumb .dk-n{font:700 10px var(--mono); color:var(--muted); width:15px; flex:none; padding-top:5px;}
  .dk-thumb.on .dk-n{color:var(--brand);}

  /* A PICTURE OF THE SLIDE, made by rendering the real slide into a fixed
     840px box and scaling it down - 840 is .slide's own max-width, so the line
     breaks are the ones the learner gets.

     ponytail: the type inside uses clamp(..., Nvh, ...), which is relative to
     the VIEWPORT and does not shrink with the transform. So a thumbnail reads
     like the slide on a short window rather than on this one. Good enough to
     recognise a slide by, which is all a thumbnail is for; the upgrade path is
     container-type:size on .dk-shot and cqh units in 05-courses.css, when a
     reason better than tidiness turns up. */
  .dk-shot{flex:1; min-width:0; height:82px; overflow:hidden; position:relative;
    border:1px solid var(--line); border-radius:8px; background:var(--bg);}
  .dk-shot-in{position:absolute; top:0; left:0; width:840px; transform:scale(.166);
    transform-origin:top left; pointer-events:none;}
  /* The slide inside a thumbnail must not stretch to the height rules the
     player gives it, or every thumbnail would be one line of text at the top of
     a very tall box. */
  .dk-shot-in .slide{min-height:0; padding:14px 18px;}

  /* ---- the stage ----
     Framed the way .pl-body frames a slide, so this is a window onto the course
     rather than a panel with the course in it.

     It does NOT scroll internally: the slide is a fixed box that never overflows,
     so a wheel over it has nothing to catch here and passes to the dialog body -
     which is what makes the wheel work while the pointer is over the slide. */
  .dk-stage{flex:none; display:flex; justify-content:center;
    padding:10px 12px 12px; background:var(--bg); border-radius:var(--radius-s);}
  .dk-page{width:100%; max-width:960px; display:flex; flex-direction:column;}
  /* The interactive learner widgets are the learner's - see Preview. */
  .dk-stage .flip, .dk-stage .scen-o, .dk-stage .hot, .dk-stage .q-o{cursor:default;}

  /* ---- editing text directly on the slide ----
     A contenteditable region is invisible until it is hovered or focused, so
     the slide reads as a slide. An empty one shows its label as a placeholder,
     drawn with ::before off the data-ph attribute so it never becomes content. */
  .dk-ed{outline:none; border-radius:6px; transition:box-shadow .12s ease, background .12s ease;
    cursor:text; min-width:2ch; display:inline-block;}
  .dk-ed:hover{box-shadow:inset 0 0 0 1px var(--line);}
  .dk-ed:focus{box-shadow:inset 0 0 0 2px var(--brand); background:var(--brand-soft);}
  .dk-ed:empty::before{content:attr(data-ph); color:var(--muted); opacity:.55; pointer-events:none;}
  /* A rich block on the stage is inert; a faint frame says "edit below". */
  .dk-rich{position:relative; border-radius:8px; box-shadow:inset 0 0 0 1px transparent;
    transition:box-shadow .12s ease;}
  .dk-rich:hover{box-shadow:inset 0 0 0 1px var(--line);}

  /* Moving, resizing, the fixed canvas and the empty-slide prompt live in
     08-deck-canvas.css - the whole free-placement surface is one concern and it
     is large enough to be its own sheet. */

  /* The slide-control toolbar used to live here as .dk-bar. It moved into the
     course header (.dk-top), styled in 08-deck-canvas.css alongside the rest of
     the full-screen chrome, so the old .dk-bar / .dk-where / .dk-ctl rules are
     gone from here. The add palette, the on-block actions and the editor popover
     also live in 08-deck-canvas.css - they are all part of the deck surface. */

  @media (max-width:1100px){
    /* Stacked, and the dialog scrolls as one again - which also brings the drag
       auto-scroll back, because .mdl-b is the scroller once more. The module
       tree lies down into a horizontal row of groups. */
    .dk{grid-template-columns:1fr;}
    .dk-strip{border-right:0; border-bottom:1px solid var(--line); max-height:none;
      flex-direction:row; overflow-x:auto; padding:0 0 12px; margin-bottom:14px; gap:18px;}
    .dk-mod-group{flex:none; min-width:200px;}
    .dk-mod-head{position:static;}
    .dk-slides{flex-direction:row; border-left:0; border-top:2px solid var(--line);
      margin-left:0; padding-left:0; padding-top:6px;}
    .dk-thumb{width:190px; flex:none;}
    .dk-main{padding:0;}
    .dk-stage{max-height:none;}
  }

  @media (max-width:900px){
    .dolist{grid-template-columns:1fr;}
    .ed{grid-template-columns:1fr;}
    .ed-side{border-right:0; border-bottom:1px solid var(--line); padding:0 0 14px; margin-bottom:14px;}
    .ed-main{padding:0;}
    header{padding-top:14px;}
    .brand-title{letter-spacing:-.3px;}
  }
  @media (max-width:640px){
    .wrap{padding:0 15px 70px;}
    .account-name{display:none;}
  }

  /* ---------- assumed test identity banner ----------
     Full width and above the top bar, on every view, because an impersonation
     you can scroll past is one you forget you are in. Amber rather than red:
     this is a state to be aware of, not a fault. */
  .assume-bar{display:flex; align-items:center; gap:12px; flex-wrap:wrap;
    padding:9px 20px; background:var(--warn-soft); color:var(--ink);
    border-bottom:1px solid color-mix(in srgb, var(--warn) 45%, transparent);}
  .assume-dot{width:8px; height:8px; border-radius:50%; flex:none; background:var(--warn);
    box-shadow:0 0 0 3px color-mix(in srgb, var(--warn) 22%, transparent);}
  .assume-txt{display:flex; flex-direction:column; line-height:1.35; min-width:0;}
  .assume-txt strong{font-weight:700; font-size:13.5px;}
  .assume-sub{color:var(--muted); font-size:12px;}
  .assume-act{margin-left:auto; display:flex; gap:8px; flex:none;}
  @media (max-width:640px){
    .assume-bar{padding:9px 14px;}
    .assume-act{margin-left:0; width:100%;}
  }

  /* ---- leaving the editor with work in hand ---------------------------
     Drawn INTO the editor's own dialog rather than as a second modal: there
     is one #modal-root and modal() replaces it, so a dialog opened over the
     editor destroys the editor it is asking about. */
  .ed-leave{position:absolute; inset:0; z-index:5; display:grid; place-items:center;
    background:var(--scrim); border-radius:inherit; animation:tourFadeIn .16s ease both;}
  .ed-leave-card{width:min(520px, calc(100% - 32px)); background:var(--surface);
    border:1px solid var(--line); border-radius:var(--radius); box-shadow:var(--shadow);
    padding:18px 20px;}
  .ed-leave-card h3{font-size:16px; margin:0 0 7px;}
  .ed-leave-card p{font-size:13.5px; color:var(--muted); margin:0; line-height:1.6;}
  @keyframes tourFadeIn{from{opacity:0} to{opacity:1}}
  /* All three on one line. They are one decision with three answers, and
     wrapping the middle one onto its own row reads as two decisions. */
  .ed-leave-foot{display:flex; gap:8px; margin-top:16px; flex-wrap:nowrap;
    justify-content:flex-end; align-items:center;}
  .ed-leave-foot .btn{white-space:nowrap; flex:0 1 auto; min-width:0;}

