html, body { height: 100%; margin: 0; padding: 0; overflow: hidden; }
body { background: #F5F6F7; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; }

#viewer {
  position: fixed;
  inset: 0;
  touch-action: manipulation;
  /* Phase 3 hotfix to Plan 03-01 — flex column lets the toolbar wrap on
     narrow viewports (e.g. Galaxy S21 @ 360px) without overflowing,
     pushes the popover into the row below the toolbar, and lets the
     page-list fill remaining space. Replaces the previous all-fixed
     layout where toolbar/popover/page-list each pinned via top/bottom
     offsets — that required the toolbar to fit in 48px no matter what,
     which broke on 360-411px viewports. */
  display: flex;
  flex-direction: column;
}

.watermark {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 9999;
  opacity: 0.12;
  background-repeat: repeat;
  background-size: 240px 160px;
  transform: translateZ(0);
}

#splash {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #F5F6F7;
  z-index: 10000;
  transition: opacity 200ms ease;
}

#splash.fade-out {
  opacity: 0;
  pointer-events: none;
}

#splash .card {
  background: #FFFFFF;
  padding: 24px;
  text-align: center;
  padding-top: max(24px, env(safe-area-inset-top));
  padding-bottom: max(24px, env(safe-area-inset-bottom));
}

#splash .spinner {
  width: 32px;
  height: 32px;
  border: 2px solid rgba(0, 0, 0, 0.1);
  border-top-color: #D31334;
  border-radius: 50%;
  animation: spin 800ms linear infinite;
  margin: 0 auto 8px;
}

#splash h1 {
  font-size: 20px;
  font-weight: 600;
  margin: 0 0 16px;
}

#splash p {
  font-size: 16px;
  font-weight: 400;
  line-height: 1.5;
  margin: 0;
  color: #444;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
  #splash .spinner {
    animation: none;
    border-top-color: rgba(0, 0, 0, 0.2);
  }
}

/* F-03 casual-copy deterrent. Trade-off documented: HTML form controls
   (input[type=search] and input[type=number] in the toolbar) override
   user-select:none in all major browsers, so the search + page-number
   inputs remain editable. The canvas + watermark inherit the rule. */
body {
  -webkit-user-select: none;
  -ms-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
}

/* PDF.js toolbar — flex item inside #viewer's flex column (Phase 3 hotfix
   refactor: was position:fixed; top:0). z-index 9998 retained as
   documentation — has no effect on a static element but signals "below
   .watermark (9999) on purpose" per the UI-SPEC contract.
   flex-wrap:wrap + min-height:48px lets the toolbar grow to a second row
   on the narrowest viewports (e.g. Galaxy S21 @ 360px) instead of
   pushing the rightmost button off-screen. */
.pdfjs-toolbar {
  flex: 0 0 auto;
  min-height: 48px;
  background: #FFFFFF;
  border-bottom: 1px solid rgba(0, 0, 0, 0.08);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
  padding: 6px 8px;
  z-index: 9998;
}

.pdfjs-toolbar button {
  background: #D31334;
  color: #FFFFFF;
  border: 0;
  padding: 6px 8px;
  border-radius: 4px;
  font: inherit;
  cursor: pointer;
  /* Phase 3 hotfix to Plan 03-01 — Pass-1 re-test #4 feedback:
     min-width:48px forced a wrap to a second row at iPhone SE (375px),
     and the operator explicitly prioritised one-row layout over the
     touch-target floor. min-width:36px lets icon buttons (zoom -/+,
     search, return chevron) shrink to their natural square footprint
     while text buttons (Prev/Next) stay at their content width. The
     resulting horizontal budget fits at iPhone SE (375), Pixel 7 (412),
     iPhone 14 Pro Max (430) and Galaxy S21 (360) in a single row.
     min-height:36px stays — height parity across text + icon buttons
     is what makes the row look uniform (text content gets ~24px
     line-height contribution that 18px svg does not). */
  min-width: 36px;
  min-height: 36px;
}

.pdfjs-toolbar button:hover { background: #B30E2C; }
.pdfjs-toolbar button:active { background: #8E0822; }

.pdfjs-toolbar input[type="number"] {
  width: 48px; /* Phase 3 hotfix — narrowed from 64px to claw back toolbar budget at 412/430px viewports; 3-digit page numbers (PDF up to 999 pages) still fit */
  padding: 6px;
  border: 1px solid rgba(0, 0, 0, 0.16);
  border-radius: 4px;
  font: inherit;
  font-size: 16px; /* Phase 3 Slice 01 — iOS auto-zoom suppression on focus */
}

/* Phase 3 hotfix to Plan 03-01 — magnifying-glass toggle in toolbar.
   Inherits padding/min-width/min-height from the base .pdfjs-toolbar
   button rule; only the flex layout (for SVG centering) is added here. */
.pdfjs-toolbar .search-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.pdfjs-toolbar .search-toggle svg { display: block; }

/* Phase 3 hotfix to Plan 03-01 — push-apart layout. The first item in
   the action cluster (zoomOutBtn in viewer.js) carries this class so
   margin-left:auto absorbs the remaining horizontal space, pushing the
   action cluster (zoom -/+, search, return) to the right edge.
   Navigation cluster (Prev / Next / page-input) stays at the left.
   When the toolbar is wide (desktop): nav-left, gap-middle, actions-right.
   When the toolbar is tight (mobile): margin-left:auto resolves to 0
   and items pack normally without forcing a wrap. */
.pdfjs-toolbar .toolbar-action-start {
  margin-left: auto;
}

/* Search popover — flex item between the toolbar and the page-list in
   #viewer's flex column. When shown it pushes the page-list down by
   its own height; when hidden it disappears from the layout entirely
   (display:none via [hidden]). Replaces the earlier position:fixed +
   top:48px shape, which broke when the toolbar wrapped to two rows
   (popover overlapped the second toolbar row). z-index 9997 is now
   informational only — flex order is the real source of truth. */
.search-popover {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  background: #FFFFFF;
  border-bottom: 1px solid rgba(0, 0, 0, 0.08);
  z-index: 9997;
}

.search-popover[hidden] { display: none; }

.search-popover input[type="search"] {
  flex: 1;
  min-width: 0; /* allow shrink past intrinsic size on narrow viewports */
  padding: 6px 10px;
  border: 1px solid rgba(0, 0, 0, 0.16);
  border-radius: 4px;
  font: inherit;
  font-size: 16px; /* Phase 3 Slice 01 — iOS auto-zoom suppression on focus (relocated from .pdfjs-toolbar input[type="search"] when search moved into popover) */
}

.search-popover .search-close {
  background: transparent;
  border: 0;
  color: #444;
  font-size: 20px;
  line-height: 1;
  padding: 4px 8px;
  cursor: pointer;
}

.search-popover .search-close:hover { color: #000; }

/* Scrollable page list — fills remaining vertical space in #viewer's
   flex column after the toolbar (and optional popover) take their
   natural heights. Phase 3 hotfix refactor: was position:fixed with
   top:48px (hardcoded to a single-row toolbar).
   position:relative restores the page-list as the offsetParent for
   the .pdfjs-page-wrapper children — currentVisiblePage() in
   viewer.js compares wrapper.offsetTop against pageList.scrollTop,
   which only works when both are in the same coordinate system.
   Without position:relative here, wrappers fall through to #viewer
   (the next positioned ancestor) and Next-button navigation gets
   stuck after page 2 (Pass-1 re-test #5 bug report). */
.pdfjs-page-list {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding: 16px;
  box-sizing: border-box;
  background: #F5F6F7;
  /* Phase 3 hotfix #11 — disable scroll anchoring inside the page list.
     Browsers try to preserve the user's visual scroll position when DOM
     children change; renderAllPages does replaceChildren() during a zoom,
     so the anchoring kicks in and fights our setScale scrollIntoView,
     leaving the user at page 1 even though we asked to restore page 50.
     overflow-anchor:none cedes scroll position control to setScale. */
  overflow-anchor: none;
}

/* Page wrapper holds the canvas + text-layer overlay. Position:relative so
   absolutely-positioned text-layer spans align against the canvas pixel grid. */
.pdfjs-page-wrapper {
  position: relative;
  margin: 0 auto 16px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
  background: #FFFFFF;
  max-width: 100%;
  /* Phase 3 hotfix to Plan 03-01 — establishes the wrapper as a container
     query context so descendant `cqw` font-size units in .pdfjs-text-layer
     resolve against the wrapper's rendered inline size. Required for
     text-layer alignment when the wrapper is CSS-downscaled (narrow
     viewports) — see buildTextLayer in viewer.js. */
  container-type: inline-size;
}

.pdfjs-page-wrapper canvas {
  display: block;
  width: 100%;
  height: 100%;
  /* F-03 drag-to-save deterrent — paired with viewer.js canvas.draggable = false */
  -webkit-user-drag: none;
}

/* Transparent text-layer overlay above the canvas. Spans are absolutely
   positioned with font-size matching the rendered glyph height, so a
   .search-match background highlights approximately the right region.
   pointer-events:none keeps clicks reaching the canvas / not blocking the
   contextmenu suppression in viewer.js. */
.pdfjs-text-layer {
  position: absolute;
  top: 0;
  left: 0;
  pointer-events: none;
  overflow: hidden;
  /* Stay above the canvas, below the watermark (.watermark is z-index 9999). */
  z-index: 1;
}

.pdfjs-text-layer span {
  position: absolute;
  white-space: pre;
  color: transparent;
  transform-origin: 0% 0%;
  border-radius: 2px;
}

.pdfjs-text-layer span.search-match {
  background: rgba(255, 200, 0, 0.45);
}

/* F-03 print blank-out. body > * { display: none } hides every direct child
   (splash, viewer, watermark, access-denied), leaving the body itself
   rendered so its ::before pseudo-element can paint the disabled message.
   The earlier `body { display: none }` form suppressed the ::before too:
   pseudo-elements inherit their host element's display state, so a hidden
   body cannot render a visible ::before. Ctrl+P / Cmd+P / right-click
   Save-as-PDF / AirPrint all hit this rule. Defense-in-depth, not bulletproof
   DRM: a user can disable site CSS via DevTools to bypass. */
@media print {
  body > * { display: none !important; }
  body::before {
    content: "Printing is disabled — open the lesson from your student desktop.";
    display: block !important;
    padding: 24px;
    font: 18px/1.4 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
    color: #000;
  }
}

/* Wordmark — TRAIN renders at heading weight (UA stylesheet bold), FITNESS
   in span.brand-light renders at normal weight to produce the canonical
   "TRAIN FITNESS" two-weight wordmark inside any h1/h2/h3 brand header. */
.brand-light { font-weight: 400; }

/* Phase 2 Slice C — Access-denied surface (D2-06, D2-07, D2-08, RESEARCH
   Pattern 6). Card chrome deliberately duplicates the #splash .card rules
   per PATTERNS.md guidance: layer isolation means a future restyle of the
   splash MUST NOT bleed into the denial layer or vice-versa. z-index 10001
   is one above #splash's 10000 — belt-and-braces per D2-08 so even if
   splash.hidden = true fails for any reason, the denial visually wins.
   No transition on this container — terminal, not progressive (RESEARCH
   Pitfall 2: don't add motion to the access-denied screen). */
#access-denied {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #F5F6F7;
  z-index: 10001;
}

/* Explicit override of the HTML hidden attribute — documents intent and
   survives a future authored rule that might set display:flex !important
   on #access-denied without checking [hidden]. */
#access-denied[hidden] { display: none; }

#access-denied .card {
  background: #FFFFFF;
  padding: 24px;
  text-align: center;
  padding-top: max(24px, env(safe-area-inset-top));
  padding-bottom: max(24px, env(safe-area-inset-bottom));
}

#access-denied h1 {
  font-size: 20px;
  font-weight: 600;
  margin: 0 0 16px;
}

#access-denied p {
  font-size: 16px;
  font-weight: 400;
  line-height: 1.5;
  margin: 0 0 16px;
  color: #444;
}

/* Standalone .return-btn ruleset — NOT scoped to .pdfjs-toolbar because
   the in-card button lives outside the toolbar. Visuals match the toolbar
   red (#D31334 / hover #B30E2C / active #8E0822) but padding is bumped
   from 6/12 to 8/16 for in-card emphasis. */
.return-btn {
  background: #D31334;
  color: #FFFFFF;
  border: 0;
  padding: 8px 16px;
  border-radius: 4px;
  font: inherit;
  cursor: pointer;
  margin-top: 8px;
}

.return-btn:hover { background: #B30E2C; }
.return-btn:active { background: #8E0822; }

/* Phase 3 hotfix to Plan 03-01 — toolbar-only override applied on top of
   .return-btn. The access-denied card's "Return to Lessons" text button
   keeps the default 8/16 padding + 8px top margin; the toolbar instance
   is an icon-button (back-chevron) with 6/12 padding to match the
   surrounding red toolbar buttons + zero top margin. Pass 1 re-test
   feedback: full "Return to Lessons" text overflowed the right edge on
   iPhone 14 Pro Max (430px); the chevron icon collapses width to match
   the search-toggle slot. */
.return-toggle {
  margin-top: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.return-toggle svg { display: block; }
