/* ============================================================
   Whimsical accents for the academic navy/gold theme.
   Presentation only -- no layout or behaviour depends on this file.
   Every animation is disabled by the prefers-reduced-motion block
   at the bottom.
   ============================================================ */

/* ---------- Page turn ----------
   The content area swings in from its left "spine" on every load,
   like turning a page in a bound book. Deliberately short so it
   never gets in the way of real work. */
@keyframes page-turn-in {
  from {
    opacity: 0;
    transform: perspective(1400px) rotateY(-5deg) translateX(-12px);
  }
  to {
    opacity: 1;
    transform: perspective(1400px) rotateY(0deg) translateX(0);
  }
}

main.container {
  transform-origin: left center;
  animation: page-turn-in 280ms cubic-bezier(0.22, 0.61, 0.36, 1) both;
}

/* ---------- Sketch line art ----------
   Scenes are drawn as stroke-only paths in a single graphite tone and
   warped by the feTurbulence filters in _sketch_defs.html. */
.sketch-defs { position: absolute; width: 0; height: 0; overflow: hidden; }

.sketch { display: block; color: var(--color-navy); }

/* Pencil writing a line -- the in-flight indicator. */
.sketch-writer {
  position: relative;
  display: inline-block;
  width: 132px;
  height: 46px;
  color: var(--color-navy);
  opacity: 0.75;
}

.sketch--pencil {
  position: absolute;
  top: 0;
  left: 0;
  width: 34px;
  height: 34px;
  animation: pencil-travel 2.6s ease-in-out infinite;
}

.sketch--written-line {
  position: absolute;
  left: 6px;
  bottom: 4px;
  width: 118px;
  height: 12px;
}

.written-line {
  stroke-dasharray: 130;
  stroke-dashoffset: 130;
  animation: sketch-draw-line 2.6s ease-in-out infinite;
}

@keyframes pencil-travel {
  0%   { transform: translateX(0) rotate(0deg); opacity: 1; }
  8%   { transform: translateX(0) rotate(-3deg); opacity: 1; }
  70%  { transform: translateX(88px) rotate(2deg); opacity: 1; }
  84%  { transform: translateX(88px) rotate(0deg); opacity: 1; }
  90%  { transform: translateX(88px) rotate(0deg); opacity: 0; }
  91%  { transform: translateX(0) rotate(0deg); opacity: 0; }
  100% { transform: translateX(0) rotate(0deg); opacity: 1; }
}

@keyframes sketch-draw-line {
  0%   { stroke-dashoffset: 130; opacity: 1; }
  70%  { stroke-dashoffset: 0; opacity: 1; }
  86%  { stroke-dashoffset: 0; opacity: 1; }
  90%  { stroke-dashoffset: 0; opacity: 0; }
  100% { stroke-dashoffset: 130; opacity: 0; }
}

/* ---------- Idle sketch in empty grid cells ----------
   An empty course-grid cell shows a faint blank-page sketch for about two
   seconds out of every twelve, gently scaling in and back out. Delays are
   assigned by cell position with deliberately uneven values so the grid
   never pulses in unison or in an obvious rotation. */
.cell-idle-sketch {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 44px;
  height: 44px;
  margin: -22px 0 0 -22px;
  color: var(--color-navy);
  opacity: 0;
  pointer-events: none;
}

.cell-idle-sketch .sketch { width: 100%; height: 100%; }

.grid-cell--empty { position: relative; }

.grid-cell--empty .cell-idle-sketch {
  animation: cell-idle-pop 12s ease-in-out infinite;
}

@keyframes cell-idle-pop {
  0%   { opacity: 0; transform: scale(0.82); }
  4%   { opacity: 0.17; transform: scale(1); }
  12%  { opacity: 0.17; transform: scale(1); }
  18%  { opacity: 0; transform: scale(0.94); }
  100% { opacity: 0; transform: scale(0.82); }
}

/* Eight-cell stagger with irregular offsets, so adjacent cells in the
   four-column grid never share a delay and the pattern doesn't repeat
   row-on-row. */
.grid-cell--empty:nth-child(8n + 1) .cell-idle-sketch { animation-delay: 0s; }
.grid-cell--empty:nth-child(8n + 2) .cell-idle-sketch { animation-delay: 5.2s; }
.grid-cell--empty:nth-child(8n + 3) .cell-idle-sketch { animation-delay: 2.4s; }
.grid-cell--empty:nth-child(8n + 4) .cell-idle-sketch { animation-delay: 9.1s; }
.grid-cell--empty:nth-child(8n + 5) .cell-idle-sketch { animation-delay: 6.8s; }
.grid-cell--empty:nth-child(8n + 6) .cell-idle-sketch { animation-delay: 1.3s; }
.grid-cell--empty:nth-child(8n + 7) .cell-idle-sketch { animation-delay: 11.4s; }
.grid-cell--empty:nth-child(8n + 8) .cell-idle-sketch { animation-delay: 3.9s; }

/* ---------- Ambient pencil GIF ----------
   Injected by ambient_gif.js, never by CSS: the asset is large, so it must
   not be fetched on page loads where it is never shown. Both appearances
   fade in and out and sit well below the content in contrast. */
.cell-gif {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 78%;
  max-width: 150px;
  transform: translate(-50%, -50%) scale(0.94);
  opacity: 0;
  pointer-events: none;
  border-radius: var(--radius);
  /* Must match CELL_FADE_MS in ambient_gif.js */
  transition: opacity 1800ms ease, transform 1800ms ease;
}
.cell-gif.is-visible {
  opacity: 0.22;
  transform: translate(-50%, -50%) scale(1);
}

.ambient-gif {
  position: fixed;
  width: 260px;
  max-width: 34vw;
  opacity: 0;
  pointer-events: none;
  z-index: 0;
  /* Must match CAMEO_FADE_MS in ambient_gif.js */
  transition: opacity 2200ms ease;
}
.ambient-gif.is-visible { opacity: 0.07; }

.ambient-gif--bottom-right { right: 2.5rem; bottom: 2.5rem; }
.ambient-gif--bottom-left { left: 2.5rem; bottom: 2.5rem; }
.ambient-gif--top-right { right: 2.5rem; top: 5.5rem; }

/* ---------- Quill loader ----------
   A nib travels left-to-right while an ink line is drawn beneath it,
   as though the page is being written. Used for form submissions. */
.quill-loader {
  display: none;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.85rem;
  color: var(--color-text-muted);
  font-size: 0.88rem;
}

.quill-loader-track {
  position: relative;
  width: 116px;
  height: 20px;
}

.quill-loader-nib {
  position: absolute;
  top: 0;
  left: 0;
  width: 18px;
  height: 18px;
  color: var(--color-navy);
  animation: quill-travel 1.4s ease-in-out infinite;
}

.quill-loader-ink {
  position: absolute;
  left: 0;
  bottom: 0;
  height: 2px;
  width: 0;
  background: var(--color-accent);
  border-radius: 2px;
  animation: quill-ink 1.4s ease-in-out infinite;
}

@keyframes quill-travel {
  0%   { transform: translateX(0) rotate(-8deg); }
  70%  { transform: translateX(96px) rotate(-8deg); }
  100% { transform: translateX(0) rotate(-8deg); }
}

@keyframes quill-ink {
  0%   { width: 0; opacity: 1; }
  70%  { width: 100px; opacity: 1; }
  75%  { width: 100px; opacity: 0; }
  100% { width: 0; opacity: 0; }
}

/* Revealed once a form marked data-animate-submit is submitted. */
form.is-submitting .quill-loader { display: inline-flex; }
form.is-submitting button[type="submit"] { opacity: 0.65; }

/* ---------- Paper aeroplane (assignment submitted) ----------
   The plane lifts up and to the right, fading as it goes. */
.plane-flight {
  display: inline-block;
  width: 20px;
  height: 20px;
  color: var(--color-teal);
  vertical-align: middle;
  margin-left: 0.4rem;
}

form.is-submitting .plane-flight {
  animation: plane-away 1.2s ease-in infinite;
}

@keyframes plane-away {
  0%   { transform: translate(0, 0) rotate(0deg); opacity: 1; }
  60%  { transform: translate(26px, -16px) rotate(12deg); opacity: 0; }
  61%  { transform: translate(-10px, 6px) rotate(0deg); opacity: 0; }
  100% { transform: translate(0, 0) rotate(0deg); opacity: 1; }
}

/* ---------- Wax seal (certificates) ----------
   Presses down from above with a slight twist, then settles. */
.wax-seal {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  color: var(--color-accent);
  animation: seal-stamp 620ms cubic-bezier(0.2, 0.9, 0.3, 1.2) both;
}

.wax-seal svg { width: 100%; height: 100%; }

@keyframes seal-stamp {
  0%   { transform: scale(1.9) rotate(-14deg); opacity: 0; }
  55%  { transform: scale(0.94) rotate(3deg); opacity: 1; }
  100% { transform: scale(1) rotate(0deg); opacity: 1; }
}

/* A soft ring pulses outward once, like the impression settling. */
.wax-seal::after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  border: 2px solid var(--color-accent);
  opacity: 0;
  animation: seal-ring 900ms ease-out 320ms both;
}

@keyframes seal-ring {
  0%   { transform: scale(0.85); opacity: 0.55; }
  100% { transform: scale(1.7); opacity: 0; }
}

.certificate-card { position: relative; }
.certificate-card .wax-seal {
  position: absolute;
  top: 0.9rem;
  right: 0.9rem;
}
/* keep the title clear of the seal */
.certificate-card h3 { padding-right: 3.4rem; }

/* ---------- Certificate unroll ----------
   Used on the certificates section heading area: the block unfurls
   downward from its top edge like a scroll being opened. */
@keyframes unroll {
  from { transform: scaleY(0.02); opacity: 0; }
  to   { transform: scaleY(1); opacity: 1; }
}

.unroll {
  transform-origin: top center;
  animation: unroll 520ms cubic-bezier(0.22, 0.61, 0.36, 1) both;
}

/* ---------- Ink check mark (survey thanks, quiz results) ----------
   The stroke draws itself as though written by hand. */
.ink-check {
  width: 64px;
  height: 64px;
  color: var(--color-teal);
}

.ink-check path {
  stroke-dasharray: 60;
  stroke-dashoffset: 60;
  animation: ink-draw 760ms ease-out 160ms both;
}

@keyframes ink-draw {
  to { stroke-dashoffset: 0; }
}

/* ---------- Flourish rise (survey thanks) ----------
   Heading and copy drift up into place under the check mark. */
@keyframes flourish-rise {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
}

.flourish { animation: flourish-rise 520ms ease-out both; }
.flourish--delay-1 { animation-delay: 320ms; }
.flourish--delay-2 { animation-delay: 440ms; }

/* ---------- Score reveal (quiz results) ----------
   The score tile fades up and its number counts into focus. */
@keyframes score-reveal {
  0%   { opacity: 0; transform: translateY(8px) scale(0.96); }
  100% { opacity: 1; transform: translateY(0) scale(1); }
}

.score-reveal { animation: score-reveal 560ms cubic-bezier(0.2, 0.9, 0.3, 1.1) both; }
.score-reveal--delay { animation-delay: 140ms; }

/* ---------- Reduced motion ----------
   One blanket override so nothing above can slip through, plus
   explicit resets for the decorative pieces that rely on an
   animation to become visible at all. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    animation-delay: 0ms !important;
    transition-duration: 0.01ms !important;
    transition-delay: 0ms !important;
    scroll-behavior: auto !important;
  }

  main.container,
  .wax-seal,
  .unroll,
  .flourish,
  .score-reveal {
    opacity: 1 !important;
    transform: none !important;
  }

  .wax-seal::after { display: none; }

  .ink-check path { stroke-dashoffset: 0 !important; }

  /* The loader still needs to communicate "working", just without motion. */
  .quill-loader-nib,
  .quill-loader-ink,
  form.is-submitting .plane-flight,
  .sketch--pencil,
  .written-line {
    animation: none !important;
  }

  .quill-loader-ink { width: 100px; }

  /* Show the pencil resting at the start of a fully drawn line, so the
     scene still reads as "writing" without any movement. */
  .sketch--pencil { opacity: 1 !important; transform: none !important; }
  .written-line { stroke-dashoffset: 0 !important; opacity: 1 !important; }

  /* The idle grid-cell sketch is purely ambient looping decoration with no
     informational value, so it is removed outright rather than frozen in
     view -- an empty cell simply stays empty. */
  .grid-cell--empty .cell-idle-sketch {
    animation: none !important;
    display: none !important;
  }

  /* The ambient GIF animates internally, so no CSS override can still it --
     ambient_gif.js refuses to inject it at all under this preference. These
     rules are a backstop in case one ever reaches the page another way. */
  .cell-gif,
  .ambient-gif {
    display: none !important;
    opacity: 0 !important;
  }
}
