/* roger.morais.cv V1.8.1: motion. Loaded only when config 'motion' allows it (see app/render.php).
   Rules of this file:
   - Every hidden starting state lives INSIDE a keyframe with fill-mode "backwards", never in a plain rule.
     If an animation does not run (old browser, reduced motion, print), the content is simply there.
   - Everything sits inside "screen and no reduced motion", so print, the PDF build and reduced-motion visitors get none of it.
   - No inline styles (the CSP forbids them): delays are written out here, positions come from CSS variables set through the CSSOM. */

@media screen and (prefers-reduced-motion: no-preference) {

  /* ---------- Page to page (EN/PT, Page/Terminal): a short cross-fade where the browser supports it ---------- */
  @view-transition { navigation: auto; }
  ::view-transition-group(root) { animation-duration: .22s; }

  /* ---------- Theme toggle: the new theme spreads from the button (class set by site.js for the length of the transition) ---------- */
  :root.vt-theme::view-transition-old(root) { animation: none; }
  :root.vt-theme::view-transition-new(root) { animation: vt-spread .55s cubic-bezier(.3, 0, .2, 1); mix-blend-mode: normal; }
  :root.vt-theme::view-transition-group(root) { animation-duration: .55s; }
  :root.vt-theme *, :root.vt-theme *::before, :root.vt-theme *::after { transition: none !important; }
  @keyframes vt-spread {
    from { clip-path: circle(0 at var(--vt-x, 100%) var(--vt-y, 0)); }
    to   { clip-path: circle(var(--vt-r, 150vmax) at var(--vt-x, 100%) var(--vt-y, 0)); }
  }

  /* =====================================================================
     OPENING, once per visit and per version (the head script sets data-intro="seen" on later page views)
     ===================================================================== */

  /* ---------- Terminal: the command types itself, then the output prints line by line ---------- */
  /* "$ man roger" is 11 characters and "$ " is already there: 9 steps from 2ch to 11ch. render.php adds .typed to the hero only
     while the eyebrow is exactly 11 characters: with other copy there is no Terminal opening at all, rather than a clipped command. */
  :root[data-skin="terminal"]:not([data-intro="seen"]) .hero.typed .eyebrow {
    align-self: flex-start; box-sizing: content-box; overflow: hidden; white-space: nowrap; /* content-box: the cursor is a border and must not eat a character */
    border-right: .6em solid transparent;
    animation: t-type .54s steps(9) .2s backwards, t-cursor .86s steps(1) backwards;
  }
  @keyframes t-type { from { width: 2ch; } to { width: 11ch; } }
  @keyframes t-cursor { from, 99% { border-right-color: var(--accent); } to { border-right-color: transparent; } }

  /* Output is printed, not faded: each line is absent, then present. */
  @keyframes t-print { from, to { opacity: 0; } }
  :root[data-skin="terminal"]:not([data-intro="seen"]) .hero.typed h1 { animation: t-print 1ms .86s backwards; }
  :root[data-skin="terminal"]:not([data-intro="seen"]) .hero.typed .lede { animation: t-print 1ms .98s backwards; }
  :root[data-skin="terminal"]:not([data-intro="seen"]) .hero.typed .cta { animation: t-print 1ms 1.1s backwards; }
  :root[data-skin="terminal"]:not([data-intro="seen"]) .hero.typed .facts div:nth-child(1) { animation: t-print 1ms 1.22s backwards; }
  :root[data-skin="terminal"]:not([data-intro="seen"]) .hero.typed .facts div:nth-child(2) { animation: t-print 1ms 1.29s backwards; }
  :root[data-skin="terminal"]:not([data-intro="seen"]) .hero.typed .facts div:nth-child(3) { animation: t-print 1ms 1.36s backwards; }
  :root[data-skin="terminal"]:not([data-intro="seen"]) .hero.typed .facts div:nth-child(4) { animation: t-print 1ms 1.43s backwards; }

  /* ---------- Editorial: the name rises out of its baseline, the rule draws, the facts settle ---------- */
  @keyframes e-fade { from { opacity: 0; } }
  @keyframes e-rise {
    /* The clip moves with the element, so its bottom inset shrinks exactly as the word climbs: the word comes up from behind a fixed line. */
    from { clip-path: inset(-10% -5% 100% -5%); transform: translateY(125%); }
    to   { clip-path: inset(-10% -5% -25% -5%); transform: none; }
  }
  @keyframes e-draw { from { transform: scaleX(0); } }
  :root[data-skin="editorial"]:not([data-intro="seen"]) .hero .eyebrow { animation: e-fade .5s ease-out .05s backwards; }
  :root[data-skin="editorial"]:not([data-intro="seen"]) .hero h1 span { animation: e-rise .8s cubic-bezier(.2, .7, .15, 1) .1s backwards; }
  :root[data-skin="editorial"]:not([data-intro="seen"]) .hero h1 span + span { animation-delay: .2s; }
  :root[data-skin="editorial"]:not([data-intro="seen"]) .hero .lede { animation: e-fade .7s ease-out .45s backwards; }
  :root[data-skin="editorial"]:not([data-intro="seen"]) .hero .cta { animation: e-fade .7s ease-out .58s backwards; }
  /* The 3px rule above the facts becomes a bar that can be drawn from the left. */
  [data-skin="editorial"] .facts { position: relative; border-top-color: transparent; }
  [data-skin="editorial"] .facts::before { content: ""; position: absolute; left: 0; right: 0; top: -3px; height: 3px; background: var(--rule); transform-origin: left; }
  :root[data-skin="editorial"]:not([data-intro="seen"]) .facts::before { animation: e-draw .7s cubic-bezier(.3, 0, .2, 1) .45s backwards; }
  :root[data-skin="editorial"]:not([data-intro="seen"]) .facts div { animation: e-fade .6s ease-out .75s backwards; }
  :root[data-skin="editorial"]:not([data-intro="seen"]) .facts div:nth-child(2) { animation-delay: .83s; }
  :root[data-skin="editorial"]:not([data-intro="seen"]) .facts div:nth-child(3) { animation-delay: .91s; }
  :root[data-skin="editorial"]:not([data-intro="seen"]) .facts div:nth-child(4) { animation-delay: .99s; }

  /* =====================================================================
     EXPERIENCE: a line beside the jobs that fills as the reader scrolls, its tip held at mid-screen.
     Scroll-driven CSS, no listeners. Browsers without it keep the V1.6 look.
     ===================================================================== */
  @supports (animation-timeline: view()) {
    #experience .jobs { position: relative; --tl-x: -24px; }
    #experience .jobs::before, #experience .jobs::after { content: ""; position: absolute; left: var(--tl-x); top: 8px; bottom: 0; width: 1px; }
    #experience .jobs::before { background: var(--rule-soft); }
    #experience .jobs::after {
      width: 2px; margin-left: -.5px; background: var(--accent); transform-origin: top;
      animation: tl-fill linear both; animation-timeline: view(); animation-range: cover 50vh cover calc(100% - 50vh);
    }
    @keyframes tl-fill { from { transform: scaleY(0); } to { transform: scaleY(1); } }

    /* One mark per job, lit when the tip of the line reaches it. */
    #experience .job { position: relative; }
    #experience .job::before {
      content: ""; position: absolute; left: calc(var(--tl-x) - 3.5px); top: var(--tl-dot, 38px); width: 8px; height: 8px; border-radius: 50%;
      background: var(--accent); box-shadow: 0 0 0 4px var(--bg);
      animation: tl-dot linear both; animation-timeline: view(); animation-range: cover 50vh cover calc(50vh + 1px);
    }
    @keyframes tl-dot { from { background: var(--rule-soft); transform: scale(.75); } to { background: var(--accent); transform: none; } }
    [data-skin="terminal"] #experience .job { --tl-dot: 34px; }
    [data-skin="terminal"] #experience .job:first-child { --tl-dot: 6px; }
  }
  @media (max-width: 639px) {
    #experience .jobs { --tl-x: -11px; }
    #experience .job { --tl-dot: 30px; }
  }

  /* =====================================================================
     NAV: a bar under the section you are reading (site.js sets aria-current and the two variables)
     ===================================================================== */
  .nav ul { position: relative; }
  .nav ul::after {
    content: ""; position: absolute; left: 0; bottom: 4px; height: 2px; width: var(--ind-w, 0px);
    transform: translateX(var(--ind-x, 0px)); background: var(--accent); opacity: var(--ind-o, 0);
    transition: transform .35s cubic-bezier(.3, 0, .2, 1), width .35s cubic-bezier(.3, 0, .2, 1), opacity .2s ease;
  }
  @media (max-width: 1079px) { [data-skin="editorial"] .nav ul::after { display: none; } }
  @media (max-width: 1199px) { [data-skin="terminal"] .nav ul::after { display: none; } }

  /* =====================================================================
     LINKS
     ===================================================================== */
  .text-link, .writing a, [data-skin="editorial"] .projects a, .foot a {
    transition: color .12s ease, text-decoration-thickness .15s ease, text-underline-offset .15s ease;
  }
  .text-link:hover, .writing a:hover, [data-skin="editorial"] .projects a:hover, .foot a:hover { text-decoration-thickness: 2px; text-underline-offset: 6px; }
  /* Terminal: a prompt mark steps in beside the project you point at. */
  [data-skin="terminal"] .projects a { position: relative; }
  [data-skin="terminal"] .projects a::before {
    content: ">"; position: absolute; left: -1.1em; top: 8px; color: var(--accent); opacity: 0; transform: translateX(-4px);
    transition: opacity .12s ease, transform .12s ease;
  }
  [data-skin="terminal"] .projects a:hover::before, [data-skin="terminal"] .projects a:focus-visible::before { opacity: 1; transform: none; }

  /* =====================================================================
     CONTACT FORM: progress on the button, a drawn check on success, a short shake on a refusal
     ===================================================================== */
  .form button.is-sending { position: relative; overflow: hidden; }
  .form button.is-sending::after {
    content: ""; position: absolute; left: 0; bottom: 0; height: 3px; width: 40%; background: var(--on-accent); opacity: .75;
    animation: f-progress 1s cubic-bezier(.4, 0, .6, 1) infinite;
  }
  @keyframes f-progress { from { transform: translateX(-100%); } to { transform: translateX(250%); } }

  .form-status.is-ok { animation: f-in .35s ease-out backwards; }
  .form-status.is-ok::before {
    content: ""; display: inline-block; width: .42em; height: .85em; margin: 0 .8em .05em .25em;
    border: solid currentColor; border-width: 0 2px 2px 0; transform: rotate(45deg);
    animation: f-check .45s cubic-bezier(.3, 0, .2, 1) .15s backwards;
  }
  /* In the mark's own (unrotated) box the short stroke is the bottom edge and the long one the right edge: bottom-up reveal draws it in writing order. */
  @keyframes f-check { from { clip-path: inset(100% 0 0 0); } to { clip-path: inset(-2px); } }
  @keyframes f-in { from { opacity: 0; transform: translateY(4px); } }
  .form-status.is-err { animation: f-shake .32s ease-in-out; }
  @keyframes f-shake { 20%, 60% { transform: translateX(-5px); } 40%, 80% { transform: translateX(5px); } }
}

/* The mark on the section being read is information, not animation: reduced-motion visitors get it too (peer review 3, L3). */
@media screen {
  .nav a[aria-current] { color: var(--accent); }
}
