/* Browser print configuration: margin 0 disables native header/footer (URL, date, page numbers) */
@page {
  size: portrait;
  margin: 0;
}

/* Counterpart to .no-print: hidden on screen, revealed only in print. Used
   by the Daily Mystery template's "Unhinged" intensity to withhold its own
   preview (see dailyMystery.js) — the visitor has to actually print to
   see it. */
.print-only {
  display: none;
}

@media print {
  body {
    background: #ffffff !important;
    color: #000000 !important;
    margin: 0 !important;
    padding: 0 !important;
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
  .no-print {
    display: none !important;
  }
  .print-only {
    display: block !important;
  }
  /* The print sheet is nested inside layout wrappers (body.min-h-screen >
     grid container with p-4/md:p-6 and gap-6 > main). Their padding, gaps
     and min-height are screen-layout concerns, but they still contribute
     real height in print — pushing the total past one page and producing a
     spurious blank second page even when the template itself fits with
     room to spare.

     This is deliberately scoped to the ancestors ABOVE the print sheet
     only. Do NOT broaden it (e.g. `body > div > div`) or add
     `display: block` — #printSheet and the template markup inside it rely
     on their own flex layout, and flattening that collapses the page. */
  html, body {
    min-height: 0 !important;
    height: auto !important;
  }
  body > div {
    padding: 0 !important;
    margin: 0 !important;
    gap: 0 !important;
    max-width: none !important;
    min-height: 0 !important;
  }
  body > div > main {
    padding: 0 !important;
    margin: 0 !important;
    min-height: 0 !important;
  }
  .print-area {
    /* Intentionally NOT forcing display:block here. #printSheet is a
       Tailwind `flex flex-col justify-between` element, and single-page
       templates rely on that flex layout to spread their content and the
       shared footer across the full physical page. Forcing block layout
       breaks that: content collapses to the top with a large blank gap
       below, since block flow doesn't distribute leftover vertical space
       the way flex-grow does. */
    box-shadow: none !important;
    border: none !important;
    width: 100% !important;
    max-width: none !important;
    margin: 0 !important;
    padding: 10mm !important;
    box-sizing: border-box !important;
    height: 98vh !important;
    max-height: 98vh !important;
    overflow: hidden !important;
    page-break-after: avoid !important;
    break-after: avoid !important;
  }
  /* Multi-page containers (Tray Feed & Certificate) break explicitly per page element.
     display:block is safe (even preferable) here since each .print-page below has
     its own forced height and doesn't need flex distribution from this parent. */
  .print-area.multi-page-mode {
    display: block !important;
    padding: 0 !important;
    height: auto !important;
    max-height: none !important;
    overflow: visible !important;
    page-break-after: auto !important;
    break-after: auto !important;
  }
  .print-page {
    page-break-after: always !important;
    break-after: page !important;
    height: 98vh !important;
    max-height: 98vh !important;
    box-sizing: border-box !important;
    padding: 10mm !important;
    margin: 0 !important;
    overflow: hidden !important;
    /* Screen shows a page outline + rounding + a dashed separator between
       pages so the preview reads as distinct sheets. On paper those are
       decorative only, and a light border prints as a visible grey line at
       the bottom of otherwise-blank pages (Tray Feed). Strip them here. */
    border: none !important;
    border-radius: 0 !important;
  }
  .print-page:last-child {
    page-break-after: avoid !important;
    break-after: avoid !important;
  }

  /* Flex items default to a "min-height: auto" floor equal to their own
     content's natural size — even inside a fixed-height, overflow:hidden
     ancestor. That lets a template's actual content silently demand more
     height than the one-page box provides; the print engine paginates
     based on that demand and spawns a second (often mostly-blank) page,
     even though .print-area would otherwise clip the overflow from view.
     Setting overflow:hidden directly on these flex items resets their
     automatic minimum size to 0 (per the flexbox spec), letting them
     actually shrink to fit instead of pushing the page taller. This is
     the single choke point every template renders through, so it fixes
     the issue site-wide without touching individual template files. */
  #templateContent,
  #globalFooterContainer,
  .print-area .flex-1,
  .print-page .flex-1 {
    overflow: hidden !important;
    min-height: 0 !important;
  }

  /* The single-page global footer used to rely on flex-grow (on
     #templateContent) computing an exact remainder that sums to precisely
     100vh. `vh` units combined with flex distribution are unreliable
     across browsers for that kind of pixel-perfect math in print contexts
     — the arithmetic can be off by enough to tip a page into a spurious
     second page just to hold the footer. Pinning the footer with absolute
     positioning against #printSheet's own box (which has a hard, fixed
     height) sidesteps that arithmetic entirely: it's placed by
     direct geometry, not by what's "left over" after content renders. */
  #globalFooterContainer {
    position: absolute !important;
    left: 10mm !important;
    right: 10mm !important;
    bottom: 10mm !important;
    margin: 0 !important;
  }
}

.font-dotmatrix {
  font-family: 'VT323', 'Courier New', monospace;
}

.font-serif-header {
  font-family: 'Cinzel', 'Georgia', serif;
}

/* Anchors a template's site-URL footer line to the physical bottom of a
   .print-page, ~1cm (10mm) up from the edge — regardless of how tall the
   page's own content happens to be. The nearest ancestor with `position:
   relative` (normally the .print-page div itself) must wrap this. */
.page-footer-url {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 10mm;
  text-align: center;
  pointer-events: none;
}

/* Template list (#sidebar): continuous tractor-feed paper. The left strip
   is painted on the list itself with background-attachment: local, so its
   holes travel with the content as it scrolls, like paper feeding through.
   The right strip is the real scrollbar: a perforated track with a light
   grey handle. Touch devices don't reliably draw styled scrollbars, so
   there the real one is hidden and the right strip is painted as a second
   background instead. */
#sidebar {
  --feed-paper: #f8fafc;
  --feed-hole: #0f172a;
  --feed-perf: #94a3b8;
  padding-left: 22px;
  background:
    linear-gradient(var(--feed-perf) 50%, transparent 50%) 16px 0 / 1px 6px repeat-y local,
    radial-gradient(circle at 8px 8px, var(--feed-hole) 3px, var(--feed-paper) 3.5px) 0 0 / 16px 16px repeat-y local;
}
#sidebar::-webkit-scrollbar { width: 16px; }
#sidebar::-webkit-scrollbar-track {
  background: radial-gradient(circle at 8px 8px, var(--feed-hole) 3px, var(--feed-paper) 3.5px) 0 0 / 16px 16px repeat-y;
  border-left: 1px dashed var(--feed-perf);
}
#sidebar::-webkit-scrollbar-thumb {
  background: radial-gradient(circle at 8px 8px, var(--feed-hole) 3px, #cbd5e1 3.5px) 0 0 / 16px 16px repeat-y;
  border-left: 1px dashed #64748b;
}
@media (hover: none) and (pointer: coarse) {
  #sidebar::-webkit-scrollbar { display: none; }
  #sidebar {
    scrollbar-width: none;
    padding-right: 22px;
    background:
      linear-gradient(var(--feed-perf) 50%, transparent 50%) 16px 0 / 1px 6px repeat-y local,
      radial-gradient(circle at 8px 8px, var(--feed-hole) 3px, var(--feed-paper) 3.5px) 0 0 / 16px 16px repeat-y local,
      linear-gradient(var(--feed-perf) 50%, transparent 50%) calc(100% - 16px) 0 / 1px 6px repeat-y local,
      radial-gradient(circle at 8px 8px, var(--feed-hole) 3px, var(--feed-paper) 3.5px) 100% 0 / 16px 16px repeat-y local;
  }
}

/* Category chip row: a thin horizontal scrollbar whose handle is the CMYK
   calibration bar printed on every test page. */
#categoryFilter::-webkit-scrollbar { height: 8px; }
#categoryFilter::-webkit-scrollbar-track { background: transparent; }
#categoryFilter::-webkit-scrollbar-thumb {
  border-radius: 3px;
  background: linear-gradient(to right, #06b6d4 0 25%, #c026d3 25% 50%, #facc15 50% 75%, #000 75% 100%);
  box-shadow: inset 0 0 0 1px #64748b;
}

/* Firefox can only colour scrollbars (no holes or stripes). Chrome ignores
   the ::-webkit rules above whenever scrollbar-color is set, so these are
   limited to browsers without ::-webkit-scrollbar. */
@supports not selector(::-webkit-scrollbar) {
  #sidebar { scrollbar-color: #cbd5e1 var(--feed-paper); }
  #categoryFilter { scrollbar-color: #c026d3 transparent; scrollbar-width: thin; }
}

/* Temple of Unhinged Testpages — sidebar button only (already inside the
   .no-print sidebar, so none of this needs its own print handling).
   Two pieces: the label's per-letter color cycling, and the black
   starfield + floating emoji layer, both gated on the button's own radio
   being checked so they only run while this template is selected. */
.tout-letter {
  display: inline-block;
}
.tout-word {
  display: inline-block;
  white-space: nowrap;
}
.template-btn:has(input:checked) .tout-letter {
  animation: dm-letter-cycle 2.6s ease-in-out infinite;
}
@keyframes dm-letter-cycle {
  0%   { color: #e34fee; }
  20%  { color: #38bdf8; }
  40%  { color: #fb7185; }
  60%  { color: #a78bfa; }
  80%  { color: #fb923c; }
  100% { color: #e34fee; }
}

/* Warp-in: plays once every time this button is (re)selected — a
   radiating light-streak zoom, like the printer just hyperspace-jumped
   you to the temple. Sits above the starfield (z-index) and fades out
   right as the starfield itself finishes fading in (see its
   transition-delay below), so one hands off to the other. */
.dm-warp {
  position: absolute;
  inset: 0;
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
  z-index: 2;
  background: repeating-conic-gradient(
    from 0deg,
    rgba(191, 219, 254, 0.9) 0deg 1.2deg,
    transparent 1.2deg 6deg
  );
}
.template-btn:has(input:checked) .dm-warp {
  animation: dm-warp-seq 1s ease-out 1;
}
@keyframes dm-warp-seq {
  0%   { opacity: 0; transform: scale(0.2); }
  15%  { opacity: 1; transform: scale(0.6); }
  70%  { opacity: 1; transform: scale(1.8); }
  100% { opacity: 0; transform: scale(2.4); }
}

.dm-starfield-layer {
  position: absolute;
  inset: 0;
  overflow: hidden;
  opacity: 0;
  transition: opacity 0.6s ease 1s;
  pointer-events: none;
  z-index: 1;
}
.template-btn:has(input:checked) .dm-starfield-layer {
  opacity: 1;
}
.dm-stars {
  position: absolute;
  inset: -20%;
  background-color: #000;
  background-repeat: repeat;
  background-size: 140px 140px;
  background-image:
    radial-gradient(1px 1px at 10% 20%, #fff, transparent 60%),
    radial-gradient(1px 1px at 30% 70%, #fff, transparent 60%),
    radial-gradient(1.5px 1.5px at 55% 15%, #fff, transparent 60%),
    radial-gradient(1px 1px at 70% 55%, #fff, transparent 60%),
    radial-gradient(1.5px 1.5px at 85% 80%, #fff, transparent 60%),
    radial-gradient(1px 1px at 95% 35%, #fff, transparent 60%),
    radial-gradient(1px 1px at 20% 90%, #fff, transparent 60%),
    radial-gradient(1px 1px at 45% 40%, #fff, transparent 60%);
  animation: dm-twinkle 3.2s ease-in-out infinite alternate;
}
@keyframes dm-twinkle {
  0%   { opacity: 0.55; }
  100% { opacity: 1; }
}
.dm-float {
  position: absolute;
  transform: translate(0, 0);
  animation-name: dm-float-drift;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
  filter: drop-shadow(0 0 2px rgba(0, 0, 0, 0.6));
}
/* Asteroids-style wraparound: a straight-line drift from one off-screen
   edge to the opposite one (--sx/--sy to --ex/--ey, set far enough out
   in dailyMystery.js to fully clear the button), then an instant snap
   back to the start at the 49.9% -> 50% boundary — no easing across that
   jump, so it reads as "reappeared on the other side" rather than a
   visible teleport-slide. */
@keyframes dm-float-drift {
  0%    { transform: translate(var(--sx), var(--sy)); }
  49.9% { transform: translate(var(--ex), var(--ey)); }
  50%   { transform: translate(var(--sx), var(--sy)); }
  100%  { transform: translate(var(--ex), var(--ey)); }
}

/* Intensity readout's top-of-range states (see intensityStatus() in
   dailyMystery.js) — Warning shakes in place, Unhinged fades/dissolves/
   reappears in a loop. Both override the plain white readout color. */
.dm-status-warning {
  color: #ef4444 !important;
  animation: dm-shake 0.15s linear infinite;
}
@keyframes dm-shake {
  0%   { transform: translate(0, 0); }
  20%  { transform: translate(-1px, 1px); }
  40%  { transform: translate(1px, -1px); }
  60%  { transform: translate(-1px, -1px); }
  80%  { transform: translate(1px, 1px); }
  100% { transform: translate(0, 0); }
}
.dm-status-unhinged {
  animation: dm-dissolve 2.2s ease-in-out infinite;
}
@keyframes dm-dissolve {
  0%   { opacity: 0; filter: blur(2px); }
  15%  { opacity: 1; filter: blur(0); }
  55%  { opacity: 1; filter: blur(0); }
  70%  { opacity: 0; filter: blur(3px); }
  85%  { opacity: 0; filter: blur(3px); }
  100% { opacity: 1; filter: blur(0); }
}
