/*--------------------------------------------------------------
# General
--------------------------------------------------------------*/
body {
  font-family: "Open Sans", sans-serif;
  color: #272829;
}

/*--------------------------------------------------------------
# Sticky footer
--------------------------------------------------------------*/
/* Short pages (404, the games lobby, the smaller tool tabs) left the footer
   floating mid-screen with dead space beneath it. Making the body a full-height
   column and letting #main absorb the slack pins the footer to the bottom when
   content is short, while still letting it be pushed down naturally when a tool
   panel grows. Not position:fixed -- that would overlay the content instead of
   following it.

   #header, .mobile-nav-toggle, .back-to-top and #preloader are all
   position:fixed, so they are out of flow and never become flex items. */
body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100dvh;
}

#main {
  flex: 1 0 auto;
  display: flex;
  flex-direction: column;
}

/* Absorbing the slack in #main alone would leave a bare white band between the
   last section and the footer. Letting the sections take it instead carries
   their background all the way down. Every direct child of #main is a section
   on all four pages. On the home page the content already overflows, so there
   is no free space to distribute and nothing changes. */
#main > section {
  flex: 1 0 auto;
}

/* The hero carries its own 100dvh height; without this it would be treated as
   shrinkable and collapse on short viewports. Since it moved inside #main it
   also matches `#main > section { flex: 1 0 auto }`, so grow is pinned back to
   0 as well: the hero should stay exactly one viewport tall and let the
   sections below it absorb any slack. */
#main > #hero {
  flex-grow: 0;
}

#hero,
#footer {
  flex-shrink: 0;
}

/* Every <img> now carries its intrinsic width/height so the browser can reserve
   the correct box before the file arrives, instead of reflowing the page around
   it. Most of these images are then sized in CSS (style="width: 50%" on the
   skill and certification logos), and the height attribute would otherwise win
   and stretch them to their full pixel height. Bootstrap gives .img-fluid this
   for free; the logos do not use it. */
img {
  height: auto;
}

a {
  color: #0563bb;
}

a:hover {
  color: #067ded;
  text-decoration: none;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  font-family: "Raleway", sans-serif;
}

/*--------------------------------------------------------------
# Back to top button
--------------------------------------------------------------*/
.back-to-top {
  position: fixed;
  display: none;
  width: 40px;
  height: 40px;
  border-radius: 50px;
  right: 15px;
  bottom: 15px;
  background: #0563bb;
  color: #fff;
  z-index: 99999;
}

.back-to-top i {
  font-size: 24px;
  position: absolute;
  top: 7px;
  left: 8px;
}

.back-to-top:hover {
  color: #fff;
  background: #0678e3;
  transition: background 0.2s ease-in-out;
}

/*--------------------------------------------------------------
# Reduced motion
--------------------------------------------------------------*/
/* The site leans hard on movement: AOS fades and zooms every section in, the
   nav and back-to-top scroll over 1.5 seconds, the hero types itself out and
   the preloader spins. For someone with a vestibular disorder that is not
   decoration, it is a trigger, and the OS setting is how they say so.

   Animations are neutralised rather than removed outright: AOS reveals content
   by transitioning opacity, so `animation: none` alone would leave every
   [data-aos] element stuck at opacity 0 and blank the page. Forcing the end
   state instead keeps everything visible and simply still.

   The JS half of this (scroll behaviour, typing) lives in assets/js/main.js. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  [data-aos] {
    opacity: 1 !important;
    transform: none !important;
  }

  /* The spinner is pure decoration and its whole point is spinning. */
  #preloader:before {
    display: none;
  }
}

/*--------------------------------------------------------------
# Button contrast
--------------------------------------------------------------*/
/* Both of these failed WCAG AA at the measured ratios below.

   .btn-primary was still Bootstrap's stock #007bff, which gives white text only
   3.97:1. It was also the one place on the site not using the brand blue, so
   moving it to #0563bb fixes the contrast (5.99:1) and the inconsistency at the
   same time.

   .btn-outline-secondary's #6c757d lands at 4.4:1 on the #f5f8fd section tint,
   which is a near miss rather than a wild one; #5a6268 takes it to 5.83:1. */
.btn-primary {
  background-color: #0563bb;
  border-color: #0563bb;
}

.btn-primary:hover,
.btn-primary:focus,
.btn-primary:not(:disabled):not(.disabled):active {
  background-color: #04529c;
  border-color: #04529c;
}

.btn-outline-secondary {
  color: #5a6268;
  border-color: #5a6268;
}

.btn-outline-secondary:hover,
.btn-outline-secondary:not(:disabled):not(.disabled):active {
  background-color: #5a6268;
  border-color: #5a6268;
  color: #fff;
}

/*--------------------------------------------------------------
# Focus visibility
--------------------------------------------------------------*/
/* The hamburger carried `outline: none !important`, which left keyboard users
   with no way to tell it had focus, and the rest of the site relied on whatever
   the UA happened to draw -- a thin default ring that all but disappears
   against the tinted section backgrounds.

   :focus-visible rather than :focus, so the ring appears for keyboard and
   assistive-tech focus but not on mouse clicks, which is what the original
   author was almost certainly trying to suppress. The two-tone outline keeps it
   legible on both the white and #f5f8fd backgrounds. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:not([tabindex="-1"]):focus-visible {
  outline: 3px solid #0563bb;
  outline-offset: 2px;
  border-radius: 2px;
}

/* Inside the nav rail the pill is already brand blue when focused, so a blue
   ring on blue would vanish. Switch to the dark ink colour there. */
.nav-menu a:focus-visible,
.mobile-nav-toggle:focus-visible {
  outline-color: #2b3742;
}

/*--------------------------------------------------------------
# Skip link
--------------------------------------------------------------*/
/* Off screen until it takes focus, rather than display:none, which would drop
   it out of the tab order and defeat the point. It has to clear the fixed nav
   rail and the hamburger, both of which sit at the top-left and top-right.

   Deliberately no transition on `top`. A transitioned property only advances
   while the page is producing animation frames, and this was observed staying
   stuck at -60px -- focused but invisible -- on a tab that had stopped
   painting. For a control whose entire purpose is to appear the moment it
   takes focus, that failure mode is not worth a 200ms slide. */
.skip-link {
  position: absolute;
  left: 8px;
  top: -60px;
  z-index: 10000;
  padding: 10px 18px;
  border-radius: 0 0 4px 4px;
  background: #0563bb;
  color: #fff;
  font-size: 15px;
  text-decoration: none;
}

.skip-link:focus {
  top: 0;
  color: #fff;
  outline: 3px solid #2b3742;
  outline-offset: 2px;
}

/* #main takes tabindex="-1" so focus really lands there when the link is
   followed. Without this some browsers scroll but leave focus behind, and the
   next Tab drops the user straight back into the nav they just skipped. */
#main:focus {
  outline: none;
}

/*--------------------------------------------------------------
# No-JavaScript fallback
--------------------------------------------------------------*/
/* Two things on this page are invisible until JavaScript says otherwise, and
   both fail closed:

   - AOS ships `opacity: 0` on every [data-aos] element and only reveals them by
     adding .aos-animate. There are 21 on the home page, which is all of About,
     Skills and Resume plus the hero.
   - #preloader is an opaque full-screen panel that main.js removes on load.

   With scripting off, or if jQuery simply fails to arrive, the result was a
   blank white page with a spinner painted on it. The failsafe in main.js cannot
   help here because it is itself JavaScript. These rules run from the
   stylesheet, so they hold in either case.

   .no-js is written into the markup on <html> and removed by assets/js/boot.js
   before paint, so it only survives when scripting is off entirely. boot.js
   additionally covers "scripts run but jQuery never arrived", and the animation
   on #preloader below covers boot.js itself failing. */
.no-js [data-aos],
.no-js [data-aos].aos-animate {
  opacity: 1 !important;
  transform: none !important;
  transition: none !important;
}

.no-js #preloader {
  display: none;
}

/*--------------------------------------------------------------
# Preloader
--------------------------------------------------------------*/
#preloader {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 9999;
  overflow: hidden;
  background: #fff;
  /* Last line of defence. This panel is opaque and covers the whole viewport,
     and it used to be removed only by jQuery on window.load -- so any failure
     to load jQuery left the site as a permanent white screen. The animation
     needs no JavaScript at all: after 8s it takes itself out of the way
     whatever else has gone wrong. main.js still fades it out on load, which is
     what normally happens, long before this fires. */
  animation: preloader-failsafe 0.4s ease-out 8s forwards;
}

@keyframes preloader-failsafe {
  to {
    opacity: 0;
    visibility: hidden;
  }
}

#preloader:before {
  content: "";
  position: fixed;
  top: calc(50% - 30px);
  left: calc(50% - 30px);
  border: 6px solid #0563bb;
  border-top-color: #fff;
  border-bottom-color: #fff;
  border-radius: 50%;
  width: 60px;
  height: 60px;
  -webkit-animation: animate-preloader 1s linear infinite;
  animation: animate-preloader 1s linear infinite;
}

@-webkit-keyframes animate-preloader {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}

@keyframes animate-preloader {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}

/*--------------------------------------------------------------
# Disable aos animation delay on mobile devices
--------------------------------------------------------------*/
@media screen and (max-width: 768px) {
  [data-aos-delay] {
    transition-delay: 0 !important;
  }
}

/*--------------------------------------------------------------
# Header
--------------------------------------------------------------*/
#header {
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  z-index: 9997;
  transition: all 0.5s;
  padding: 15px;
  overflow-y: auto;
}

/* 991.98px, not 992px: `max-width: 992px` and `min-width: 992px` both match at
   exactly 992, so a viewport of that width got the off-canvas drawer and the
   desktop icon rail at the same time. */
@media (max-width: 991.98px) {
  #header {
    width: 300px;
    background: #fff;
    border-right: 1px solid #e6e9ec;
    left: -300px;
  }
}

/* The hamburger belongs to the off-canvas drawer, so it has to disappear at the
   same width the drawer does. It used to carry Bootstrap's .d-xl-none, which
   hides at 1200px -- 200px later than this nav switches. Between 993 and 1199px
   both the icon rail and the hamburger were on screen, and pressing it added
   .mobile-nav-active: that locks body scroll while #header is already at left:0,
   so the page froze and nothing appeared to open. */
@media (min-width: 992px) {
  .mobile-nav-toggle {
    display: none;
  }
}

@media (min-width: 992px) {
  /* The nav rail is position:fixed and paints no background of its own on
     desktop. Offsetting #main left a 100px strip of bare body colour beside
     every tinted section -- a hard vertical seam down the page, most obvious on
     Tools and Games and on the Skills section at home.

     The offset therefore lives on the sections, not on #main: each section
     stretches the full width so its background runs underneath the rail (as the
     footer already did), and its own left padding holds the content in exactly
     the same place as before. */
  #main > section {
    padding-left: 100px;
  }

  /* The hero is now a child of #main so that landmark navigation reaches the
     name and job title, which previously sat outside every landmark. That put
     it in range of the rule above, whose `#main > section` specificity beats a
     bare `#hero`, so its wider offset has to be restated at matching weight. */
  #main > #hero {
    padding-left: 160px;
  }
}

/*--------------------------------------------------------------
# Navigation Menu
--------------------------------------------------------------*/
/* Desktop Navigation */
.nav-menu * {
  margin: 0;
  padding: 0;
  list-style: none;
}

.nav-menu > ul > li {
  position: relative;
  white-space: nowrap;
}

.nav-menu a {
  display: flex;
  align-items: center;
  color: #45505b;
  padding: 10px 18px;
  margin-bottom: 8px;
  transition: 0.3s;
  font-size: 15px;
  border-radius: 50px;
  background: #f2f3f5;
  height: 56px;
  width: 100%;
  overflow: hidden;
  transition: 0.3s;
}

.nav-menu a i {
  font-size: 20px;
}

.nav-menu a span {
  padding: 0 5px 0 7px;
  color: #45505b;
}

@media (min-width: 992px) {
  .nav-menu a {
    width: 56px;
  }
  .nav-menu a span {
    display: none;
    color: #fff;
  }
}

.nav-menu a:hover,
.nav-menu .active > a,
.nav-menu li:hover > a {
  color: #fff;
  background: #0563bb;
}

.nav-menu a:hover span,
.nav-menu .active > a span,
.nav-menu li:hover > a span {
  color: #fff;
}

.nav-menu a:hover,
.nav-menu li:hover > a {
  width: 100%;
  color: #fff;
}

.nav-menu a:hover span,
.nav-menu li:hover > a span {
  display: block;
}

/* Mobile Navigation */
.mobile-nav-toggle {
  position: fixed;
  right: 15px;
  top: 15px;
  z-index: 9998;
  border: 0;
  background: none;
  font-size: 24px;
  transition: all 0.4s;
  line-height: 1;
  cursor: pointer;
  text-align: right;
}

.mobile-nav-toggle i {
  color: #45505b;
}

.mobile-nav-active {
  overflow: hidden;
}

.mobile-nav-active #header {
  left: 0;
}

.mobile-nav-active .mobile-nav-toggle i {
  color: #0563bb;
}

/*--------------------------------------------------------------
# Hero Section
--------------------------------------------------------------*/
#hero {
  width: 100%;
  /* 100vh ignores the mobile browser's collapsing toolbars, so the hero runs
     taller than the visible viewport and pushes its own content off screen.
     dvh tracks the real height; the vh line stays as the fallback. */
  height: 100vh;
  height: 100dvh;
  background: url("../img/hero-bg.webp") top right no-repeat;
  background-size: cover;
  position: relative;
}

@media (min-width: 992px) {
  #hero {
    padding-left: 160px;
  }
}

#hero:before {
  content: "";
  background: rgba(255, 255, 255, 0.8);
  position: absolute;
  bottom: 0;
  top: 0;
  left: 0;
  right: 0;
}

#hero h1 {
  margin: 0;
  font-size: 64px;
  font-weight: 700;
  /* Was 56px, i.e. tighter than the type itself, which clipped descenders and
     would have collided outright had the name ever wrapped to two lines. */
  line-height: 1.15;
  color: #45505b;
}

#hero p {
  color: #45505b;
  margin: 15px 0 0 0;
  font-size: 26px;
  font-family: "Poppins", sans-serif;
}

#hero p span {
  color: #0563bb;
  letter-spacing: 1px;
}

#hero .social-links {
  margin-top: 30px;
}

#hero .social-links a {
  font-size: 24px;
  display: inline-block;
  color: #45505b;
  line-height: 1;
  margin-right: 20px;
  transition: 0.3s;
}

#hero .social-links a:hover {
  color: #0563bb;
}

@media (max-width: 991.98px) {
  #hero {
    text-align: center;
  }
  #hero h1 {
    font-size: 32px;
    line-height: 1.15;
  }
  #hero p {
    margin-top: 10px;
    font-size: 20px;
    line-height: 24px;
  }
}

/*--------------------------------------------------------------
# Sections General
--------------------------------------------------------------*/
section {
  padding: 60px 0;
  overflow: hidden;
}

/* Alternating tint that separates one section from the next. The markup has
   carried this class since the template it came from, but the rule itself was
   missing, so every section rendered flat white. */
.section-bg {
  background: #f5f8fd;
}

.section-title {
  text-align: center;
  padding-bottom: 30px;
}

/* h1 on the standalone Tools/Games pages, h2 for sections within the home page. */
.section-title h1,
.section-title h2 {
  font-size: 32px;
  font-weight: bold;
  text-transform: uppercase;
  margin-bottom: 20px;
  padding-bottom: 20px;
  position: relative;
  color: #45505b;
}

.section-title h1::before,
.section-title h2::before {
  content: "";
  position: absolute;
  display: block;
  width: 120px;
  height: 1px;
  background: #ddd;
  bottom: 1px;
  left: calc(50% - 60px);
}

.section-title h1::after,
.section-title h2::after {
  content: "";
  position: absolute;
  display: block;
  width: 40px;
  height: 3px;
  background: #0563bb;
  bottom: 0;
  left: calc(50% - 20px);
}

.section-title p {
  margin-bottom: 0;
}

/*--------------------------------------------------------------
# About
--------------------------------------------------------------*/
.about .content h3 {
  font-weight: 700;
  font-size: 26px;
  color: #728394;
}

.about .content ul {
  list-style: none;
  padding: 0;
}

.about .content ul li {
  padding-bottom: 10px;
}

.about .content ul i {
  font-size: 20px;
  padding-right: 2px;
  color: #0563bb;
}

.about .content p:last-child {
  margin-bottom: 0;
}

/*--------------------------------------------------------------
# Skills
--------------------------------------------------------------*/
.skills .progress {
  height: 50px;
  display: block;
  background: none;
}

.skills .progress .skill {
  padding: 10px 0;
  margin: 0 0 6px 0;
  text-transform: uppercase;
  display: block;
  font-weight: 600;
  font-family: "Poppins", sans-serif;
  color: #45505b;
}

.skills .progress .skill .val {
  float: right;
  font-style: normal;
}

.skills .progress-bar-wrap {
  background: #f2f3f5;
}

.skills .progress-bar {
  width: 1px;
  height: 10px;
  transition: 0.9s;
  background-color: #0563bb;
}

.skills .logo {
  display: flex;
  text-align: center;
  align-items: center;
  flex-wrap: wrap;
}

/*--------------------------------------------------------------
# Resume
--------------------------------------------------------------*/
.resume .resume-title {
  font-size: 26px;
  font-weight: 700;
  margin-top: 20px;
  margin-bottom: 20px;
  color: #45505b;
}

.resume .resume-item {
  padding: 0 0 20px 20px;
  margin-top: -2px;
  border-left: 2px solid #0563bb;
  position: relative;
}

.resume .resume-item h4 {
  line-height: 18px;
  font-size: 18px;
  font-weight: 600;
  text-transform: uppercase;
  font-family: "Poppins", sans-serif;
  color: #0563bb;
  margin-bottom: 10px;
}

/* Dates and subtitles, not headings. They were <h5> purely for the styling,
   which put seven fake entries into the document outline that a screen reader
   user navigating by heading had to step through to reach the real content. */
.resume .resume-item .resume-meta {
  font-size: 16px;
  background: #f7f8f9;
  padding: 5px 15px;
  display: inline-block;
  font-weight: 600;
  margin-bottom: 10px;
}

.resume .resume-item ul {
  padding-left: 20px;
}

.resume .resume-item ul li {
  padding-bottom: 10px;
}

.resume .resume-item:last-child {
  padding-bottom: 0;
}

.resume .resume-item::before {
  content: "";
  position: absolute;
  width: 16px;
  height: 16px;
  border-radius: 50px;
  left: -9px;
  top: 0;
  background: #fff;
  border: 2px solid #0563bb;
}

/*--------------------------------------------------------------
# Footer
--------------------------------------------------------------*/
#footer {
  background: #f7f8f9;
  color: #45505b;
  font-size: 14px;
  text-align: center;
  padding: 30px 0;
}

/* Every section gets a 100px left offset to clear the fixed nav rail, but the
   footer did not, so its centred content sat 50px left of everything above it
   -- a visible drift on all four pages. This has to come after the shorthand
   above, which would otherwise reset padding-left back to 0. */
@media (min-width: 992px) {
  #footer {
    padding-left: 100px;
  }
}

#footer h3 {
  font-size: 36px;
  font-weight: 700;
  position: relative;
  font-family: "Poppins", sans-serif;
  padding: 0;
  margin: 0 0 15px 0;
}

#footer p {
  font-size: 15px;
  font-style: italic;
  padding: 0;
  margin: 0 0 40px 0;
}

#footer .social-links {
  margin: 0 0 40px 0;
}

#footer .social-links a {
  font-size: 18px;
  display: inline-block;
  background: #0563bb;
  color: #fff;
  line-height: 1;
  padding: 8px 0;
  margin-right: 4px;
  border-radius: 50%;
  text-align: center;
  width: 36px;
  height: 36px;
  transition: 0.3s;
}

#footer .social-links a:hover {
  background: #0678e3;
  color: #fff;
  text-decoration: none;
}

#footer .copyright {
  margin: 0 0 5px 0;
}

#footer .credits {
  font-size: 13px;
}
