/* web-courses.css - PLAN 006, COURSES AND THE LESSON WORKSPACE, WEB ONLY.
   Loaded by member.html AFTER web-surface.css. It is an ADDITIVE OVERRIDE LAYER: it changes no existing
   rule, no artwork, no lock, no progress write, no URL and no media element. It only re-states geometry
   for the one surface that carries :root[data-ui-surface="web-app"].

   EVERY RULE IN THIS FILE IS SCOPED UNDER THAT MARKER, WITH NO EXCEPTIONS AT ALL. web-surface.css is
   allowed three unscoped lines (the --web-surface-css sentinel and the two #webRail/#webScrim hidden
   backstops) because those are provably unreachable by native. This file needs none of them: every
   selector here names a class that the packaged Android app, the packaged iOS app, the marketing shell,
   the floating launcher and the in-course embeds all construct too. An unscoped rule here would land on
   all five. _web-courses-check.mjs asserts, over the real bytes, that no rule in this file is unscoped,
   and it proves that assertion can fail by feeding it an injected unscoped rule.

   WHAT THIS FILE DELIBERATELY DOES NOT DO
   - It does not touch .crs-ico img. Aye re-cut that on 2026-09-07 (D1, vc118: contain + a 6px inset so
     the mark stops being clipped by the disc's border-radius). The 46px DISC is the contract's number and
     it already matches, so the token below re-states 46px rather than changing it - the artwork rule is
     his, not this plan's.
   - It does not create, move, clone or re-parent a media node. The grid below places .lesson-player,
     .lesson-current and .lesson-curriculum - three siblings playLesson() already emits (member.html:8214,
     8222, 8229) - into two columns. Crossing the breakpoint is a CSS reflow of nodes that never move in
     the DOM, so <video>/<audio> never re-attach, never re-request the signed URL and never lose
     currentTime. That is the whole reason this is a stylesheet and not a re-render.
   - It sets no rule on #crsX. The exit's glyph, label and parent-navigation callback are unchanged. */

/* ---- TOKENS -------------------------------------------------------------------------------------
   Contract table values for Courses, declared inside the marker so nothing outside the web app can read
   them, let alone inherit them. No base token is redefined. */
:root[data-ui-surface="web-app"]{
  --web-course-max:1120px;
  --web-curriculum-width:320px;
  --web-course-gap:24px;
  --web-course-row:72px;
  --web-course-art:46px;
}

/* ---- THE COURSE PAGE FRAME ----------------------------------------------------------------------
   --crspad is the ONE place this file's side padding is written, exactly as the base rule intends
   (member.html:1210). .crs .vwrap breaks the player out by -1 * --crspad, so moving 34 -> 24 keeps the
   full-bleed player exact by construction instead of by arithmetic - the same property the base comment
   was written to protect.

   THE CONTAINER IS ON .crs-body ON PURPOSE, AND THE PLAN IS EXPLICIT ABOUT WHY. The lesson grid must key
   off the AVAILABLE CONTENT BOX after the shell's sidebar offset and this element's own padding, not off
   the viewport: a 1440px browser with a 240px rail open has a different content width than a 1440px
   browser with the rail collapsed, and a physical-width media query cannot tell those apart.
   container-type:inline-size queries the content box, which is precisely that number.

   inline-size containment is safe here: .crs-body is flex:1 with width:100% inside a column flex parent,
   so its inline size is already imposed from outside and never derived from its contents. It brings
   layout+style containment with it, NOT paint containment, so native <video> fullscreen (top layer) is
   untouched, and every absolutely positioned descendant in this subtree already has a positioned parent
   inside it (.vload -> .vwrap, .hovisPtrTip -> .hovisPtrHost, .crs-course-loading -> .crs-course-stage). */
:root[data-ui-surface="web-app"] .crs-body{
  --crspad:24px;
  padding:24px var(--crspad) 32px;
  container-name:hovis-web-course;
  container-type:inline-size;
}

:root[data-ui-surface="web-app"] .crs-hin,
:root[data-ui-surface="web-app"] .crs-inner{ max-width:var(--web-course-max) }

/* THE SAFE-AREA INSET STAYS IN THE OVERRIDE. The plan's snippet writes a flat `padding:6px 24px`, which
   is a full reset of a shorthand that carries var(--sat) - and member.html:1487 already records what that
   costs: an override that dropped the inset put the Courses header back under the status bar and Aye
   photographed it. --sat is 0 in a desktop browser, so the contract's 56px is unaffected there, but the
   marker deliberately INCLUDES an installed browser PWA, which can be a standalone iOS window with a real
   notch. Keeping the inset costs nothing on the surface the number was written for and protects the one
   web surface that has an inset. */
:root[data-ui-surface="web-app"] .crs-head{
  padding:calc(6px + var(--sat)) 24px 6px;
  min-height:56px;
}
/* white-space:normal is the "no forced truncation" half of the plan: the base rule ellipsises a long
   course name onto one line, and at 24px there is room to wrap instead. */
:root[data-ui-surface="web-app"] .crs-title{
  font-size:24px;
  line-height:30px;
  white-space:normal;
}

/* ---- THE COURSE LIBRARY ROW ---------------------------------------------------------------------
   One flat list, unchanged in structure. min-height (not height) is what lets a long title grow the row
   instead of squeezing it, and the 46px disc + 12px padding = 70px sits inside the 72px floor with room
   to spare, so the artwork is never the thing that sets the row height. */
:root[data-ui-surface="web-app"] .crs-row{
  min-height:var(--web-course-row);
  padding:12px;
  gap:12px;
}
:root[data-ui-surface="web-app"] .crs-ico{
  width:var(--web-course-art);
  height:var(--web-course-art);
}
:root[data-ui-surface="web-app"] .crs-row .n{ font-size:14px; line-height:20px }
:root[data-ui-surface="web-app"] .crs-row .m{ font-size:12.5px; line-height:18px }

/* The web course screen opts into the shell palette without changing homepage/embed or native Apple.
   Locked art stays branded; the lock text and action already carry the state without a gray duplicate. */
:root[data-ui-surface="web-app"] .vload{ background:var(--bg) }
:root[data-ui-surface="web-app"] .vload.biz{ background:var(--side) }
:root[data-ui-surface="web-app"] .crs-row.lockedrow .crs-ico{ opacity:1; filter:none }

/* One flat hierarchy around native media. The row remains the sole action and its existing .res-a span
   remains the one Playing/Paused/Completed status source, now label-like instead of a second button. */
:root[data-ui-surface="web-app"] .crs video{ box-shadow:none }
:root[data-ui-surface="web-app"] .audbox{ border:0; background:var(--side) }
:root[data-ui-surface="web-app"] .crs-acc{ border:0; background:transparent }
:root[data-ui-surface="web-app"] .lesson-curriculum .res-a{
  border:0; border-radius:0; padding:0; background:transparent; color:var(--mut);
}
:root[data-ui-surface="web-app"] .lesson-curriculum .crs-row.cur .res-a{ color:var(--accent-deep) }

/* ---- THE WIDE LESSON WORKSPACE ------------------------------------------------------------------
   Two selectors for one layout, and the second is not decoration:

   .crs-inner:has(> .lesson-player) is the CSS-only way to say "this frame is a lesson". It reads the
   exact fact playLesson() emits, with no observer to run, nothing to tear down on a frame rebuild, and
   no window in which the class is missing.

   .crs-inner.web-lesson-layout is kept alongside it so that if the shell later gains the class the plan
   describes, this layer answers it without a second edit. Neither selector can match a non-lesson frame:
   the library, the citation preview and the loading cover emit no .lesson-player. */
/* 940px, NOT 1040px, AND THE OLD NUMBER WAS A MEASURED DEFECT - the two-column workspace never once
   appeared on a 1280px laptop, which is the width this layout was written for.
   WHY 1040 COULD NOT FIRE. The query measures .crs-body's CONTENT box, which is what container-type:
   inline-size means. On a 1280px browser in the default nav state that box is
     1280 - 240 (the expanded sidebar offset on .main) - 48 (this file's own 24px side padding) = 992px,
   so the threshold sat 48px above the widest content box a 1280px screen can produce. Rendered and
   measured before this change (_web-courses-drive-check.mjs at 1280): grid-template-columns computed
   `none`, .lesson-curriculum sat at the same x as .lesson-player with position:static, and the plan's
   inline-margin override never applied either because it lives inside this block. 1040 reads like the
   .main width (1280-240) with the padding forgotten.
   WHY 940 IS THE RIGHT NUMBER AND NOT SIMPLY A SMALLER ONE. The two columns are the 320px curriculum
   plus the 24px gap, so 940 leaves the player 596px at the moment the layout turns on and 648px at
   1280 - a real video column, not a sliver. It also keeps the bands the contract asks for intact,
   because the number is checked from BOTH sides: at the 900-1199 rail band a 1000px browser has a
   944px .main and an 896px content box, which is still BELOW 940, so the rail band and every phone
   width keep the stacked one-column layout exactly as before. Verified at 1280/1000/700/390. */
@container hovis-web-course (min-width:940px){
  :root[data-ui-surface="web-app"] .crs-inner:has(> .lesson-player),
  :root[data-ui-surface="web-app"] .crs-inner.web-lesson-layout{
    display:grid;
    grid-template-columns:minmax(0,1fr) var(--web-curriculum-width);
    /* `auto 1fr`, NOT the default `auto auto`, AND THIS WAS A RENDERED DEFECT, NOT A PREFERENCE.
       The curriculum spans both rows. With two auto rows, a curriculum taller than the player plus the
       current-lesson block has its surplus height DISTRIBUTED EQUALLY between the two rows it spans - so
       an eight-lesson list opened a ~50px dead band between the audio player and the lesson title, and
       another under the title, on a page whose whole point is that the two sit together. Measured at
       1920x1080 before this line: 90px of empty space under .audbox. A flexible second row absorbs the
       surplus below the title instead of splitting it above. */
    grid-template-rows:auto 1fr;
    gap:16px var(--web-course-gap);
    align-items:start;
  }
  /* DOM order is untouched. The grid places the three existing siblings; it does not reorder them for
     assistive technology or for Tab, because grid-column/grid-row are placement, not source order. */
  :root[data-ui-surface="web-app"] .crs-inner:has(> .lesson-player) > .lesson-player,
  :root[data-ui-surface="web-app"] .crs-inner.web-lesson-layout > .lesson-player{
    grid-column:1; grid-row:1; min-width:0;
  }
  :root[data-ui-surface="web-app"] .crs-inner:has(> .lesson-player) > .lesson-current,
  :root[data-ui-surface="web-app"] .crs-inner.web-lesson-layout > .lesson-current{
    grid-column:1; grid-row:2; min-width:0;
  }
  /* sticky to the top of .crs-body's own scrollport - the course area, not the window - with a normal
     vertical scrollbar of its own. align-items:start above is what leaves it travel to stick within. */
  :root[data-ui-surface="web-app"] .crs-inner:has(> .lesson-player) > .lesson-curriculum,
  :root[data-ui-surface="web-app"] .crs-inner.web-lesson-layout > .lesson-curriculum{
    grid-column:2; grid-row:1 / span 2; min-width:0;
    position:sticky; top:0;
    max-height:calc(100dvh - 136px);
    overflow:auto;
    padding:0;
  }
  /* THE ONE !important IN THIS FILE, AND IT IS BEATING AN INLINE STYLE, NOT A STYLESHEET.
     playLesson() writes style="margin-top:22px" directly on the curriculum's "In this course" heading
     (member.html:8229). An inline declaration outranks every selector, so this is the only way to reach
     it without rewriting the existing lesson markup. It is confined to the wide branch: in the
     one-column layout that 22px is the real separation between the lesson block and the list below it
     and must stay. In the two-column layout the flat list aligns directly with the player, so that
     inherited 22px is dead space. */
  :root[data-ui-surface="web-app"] .crs-inner:has(> .lesson-player) > .lesson-curriculum > .resgrp-h,
  :root[data-ui-surface="web-app"] .crs-inner.web-lesson-layout > .lesson-curriculum > .resgrp-h{
    margin-top:0 !important;
  }

  /* THE LESSON ROW REFLOWS INSIDE THE 320px RAIL, AND THIS IS A LEGIBILITY FIX I MEASURED, NOT A STYLE
     CHOICE. The row is mark + title/duration + a state pill, and .res-a is white-space:nowrap
     (member.html:1143), so at 320px the pill takes ~110px of a 280px content box and the title is left
     with about 120px. Rendered at 1920x1080 before this rule, the current lesson's title wrapped onto
     SEVEN lines beside a two-word pill - see lesson-1920-AFTER-before-rowfix.png. The plan's own
     acceptance asks that current-lesson text stay legible, so the pill moves to its own line under the
     title and the title gets the full column.

     Nothing is added, removed, reworded or unhooked: the same three children, the same row element, the
     same click and Enter/Space handlers, the same tab stop. Only inside the wide two-column branch - the
     one-column layout keeps today's single-line row exactly. */
  :root[data-ui-surface="web-app"] .crs-inner:has(> .lesson-player) > .lesson-curriculum .crs-row,
  :root[data-ui-surface="web-app"] .crs-inner.web-lesson-layout > .lesson-curriculum .crs-row{
    display:grid;
    grid-template-columns:26px minmax(0,1fr);
    align-items:center;
    column-gap:12px;
    row-gap:6px;
  }
  /* Explicit column, auto row: it lands in column 2 of the next free row, i.e. under the title rather
     than under the tick. */
  :root[data-ui-surface="web-app"] .crs-inner:has(> .lesson-player) > .lesson-curriculum .crs-row > .res-a,
  :root[data-ui-surface="web-app"] .crs-inner.web-lesson-layout > .lesson-curriculum .crs-row > .res-a{
    grid-column:2;
    justify-self:start;
  }
}

/* ---- NARROW ------------------------------------------------------------------------------------- */
@media (max-width:599px){
  :root[data-ui-surface="web-app"] .crs-body{ --crspad:16px; padding-top:16px }
  :root[data-ui-surface="web-app"] .crs-head{ padding-inline:16px }
  :root[data-ui-surface="web-app"] .crs-title{ font-size:22px; line-height:28px }
}

/* ---- A PHONE TURNED SIDEWAYS --------------------------------------------------------------------
   THIS BLOCK EXISTS TO PRESERVE BEHAVIOUR, NOT TO ADD ANY. member.html:1287-1301 slims the course header
   to ~53px in short landscape for one measured reason: it lets `.crs video{max-height:calc(100vh - 58px)}`
   grow the picture from 537x302 to about 665x374 on an 844x390 screen. That base rule's specificity is
   (0,1,0); every rule above is (0,2,1) and would silently beat it, which would put a 24px title and 56px
   header back on the screen where the video is the entire point and shrink the picture again. So the
   marker re-states the slim header at its own specificity. Last in the file so it wins the overlap with
   the narrow block on a small landscape window. The video rules themselves are untouched. */
@media (orientation:landscape) and (max-height:520px){
  :root[data-ui-surface="web-app"] .crs-body{ --crspad:16px; padding-top:0 }
  :root[data-ui-surface="web-app"] .crs-head{ padding:calc(4px + var(--sat)) 14px 4px; min-height:52px }
  :root[data-ui-surface="web-app"] .crs-title{ font-size:var(--fs-body); line-height:1.25 }
}
