/* ==========================================================================
   Base
   ========================================================================== */

html {
  scroll-behavior: smooth;
}

/* Eased alpha ramps used to feather the panel photos into the page at each
   seam. Written out as stops because a linear fade leaves a visible edge. */
:root {
  --feather-in: rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.1) 4%,
    rgba(0, 0, 0, 0.32) 8%, rgba(0, 0, 0, 0.62) 12%, rgba(0, 0, 0, 0.86) 16%,
    rgba(0, 0, 0, 0.97) 19%, rgba(0, 0, 0, 1) 22%;
  --feather-out: rgba(0, 0, 0, 1) 78%, rgba(0, 0, 0, 0.97) 81%,
    rgba(0, 0, 0, 0.86) 84%, rgba(0, 0, 0, 0.62) 88%, rgba(0, 0, 0, 0.32) 92%,
    rgba(0, 0, 0, 0.1) 96%, rgba(0, 0, 0, 0) 100%;

  --feather-both: linear-gradient(
    to bottom,
    var(--feather-in),
    var(--feather-out)
  );
  --feather-bottom: linear-gradient(
    to bottom,
    rgba(0, 0, 0, 1) 0%,
    var(--feather-out)
  );
  --feather-top: linear-gradient(
    to bottom,
    var(--feather-in),
    rgba(0, 0, 0, 1) 100%
  );

  /* Spacing shared by the three panels below the landing screen, so their
     titles line up with each other as you scroll and the rhythm inside each
     block is the same. Each panel still chooses its own column to stay clear
     of its photo; only the offsets within that column are shared. */
  --content-top: 24vh;
  --content-inset: 16px;
  --text-gap: 16px;
  --control-gap: 26px;
  --icon-gap: 18px;
}

/* Phones get their own offset, but still a share of the height rather than a
   fixed pixel value: the photos are sized to cover the panel, so they scale
   with it, and a fixed offset would push the copy proportionally deeper into
   the cat on a short handset than on a tall one. */
@media (max-width: 38.75em) {
  :root {
    --content-top: 18vh;
  }
}

/* One placement rule for all three content blocks. Each spans the full height
   of its panel and is pushed down by the same offset, rather than being
   centred inside a grid row — centring made the title's position depend on how
   much copy sat under it, which is what knocked the three out of alignment. */
.about__content,
.resume__content,
.contact__inner {
  grid-row: 1 / -1;
  align-self: start;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  margin-top: var(--content-top);
  padding-left: var(--content-inset);
  max-width: 1040px;
  color: #f4f8fb;
}

/* The title/copy sub-block opts back into the full column width so its 75%
   measure resolves against the column and not its own content. */
.about__inner {
  align-self: stretch;
}

.about__text,
.contact__text,
.resume__text {
  margin-top: var(--text-gap);
}

.about__buttons,
.contact__icons,
.resume__icons {
  margin-top: var(--control-gap);
}

.about__buttons > a {
  margin-left: 0px;
}

body {
  margin: 0;
  padding: 0;
  font-family: 'Rubik', sans-serif;
  /* The photos feather out to nothing at each seam, so what shows through
     between them needs to be black rather than the default white. */
  background-color: #000000;
}

/* Every section is a full-viewport grid over a cover-fitted photo. The photo
   lives on ::before rather than the panel itself so it can be scaled without
   dragging the content along with it. */
.panel {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: grid;
  width: 100%;
  height: 100vh;
}

.panel::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background-repeat: no-repeat;
  background-position: center;
  background-size: cover;

  /* Dissolve the top and bottom edges so consecutive photos melt into each
     other through the black page behind them, instead of meeting at a hard
     line. The extra stops ease the falloff; a plain two-stop ramp still reads
     as a visible edge where a bright part of a photo meets the seam. */
  -webkit-mask-image: var(--feather-both);
  mask-image: var(--feather-both);
}

/* The very top and very bottom of the page have nothing to blend into, so
   those two outer edges stay solid. */
.panel--header::before {
  -webkit-mask-image: var(--feather-bottom);
  mask-image: var(--feather-bottom);
}

.panel--contact::before {
  -webkit-mask-image: var(--feather-top);
  mask-image: var(--feather-top);
}

.separator {
  height: 4px;
  width: var(--separator-width);
  background: #f4f8fb;
}

/* ==========================================================================
   Button

   Mirrors the Material UI v4 Button the React version used, plus the
   styled-components override that was applied to every instance.
   ========================================================================== */

/* ==========================================================================
   Motion

   Everything here is opt-in: the reveal rules only apply once main.js has set
   the `js` class, so with scripting off the content is simply visible. All of
   it sits behind prefers-reduced-motion.
   ========================================================================== */

@media (prefers-reduced-motion: no-preference) {
  /* Panels fade up as they scroll into view. main.js stages the delay on each
     child so they arrive in document order rather than all at once. */
  .js [data-reveal] {
    opacity: 0;
    transform: translateY(14px);
    transition: opacity 600ms cubic-bezier(0.4, 0, 0.2, 1),
      transform 600ms cubic-bezier(0.4, 0, 0.2, 1);
  }

  .js .panel.is-visible [data-reveal] {
    opacity: 1;
    transform: none;
  }

  /* The separators draw themselves out from nothing. */
  .js .separator {
    width: 0;
    transition: width 0.5s ease-in 250ms;
  }

  .js .panel.is-visible .separator {
    width: var(--separator-width);
  }

  /* A slow drift across each photo. Durations are deliberately mismatched so
     the four panels never fall into step with one another. */
  .panel::before {
    animation: panel-drift 32s ease-in-out infinite alternate;
  }

  .panel--about::before {
    animation-duration: 38s;
    animation-direction: alternate-reverse;
  }

  .panel--resume::before {
    animation-duration: 30s;
  }

  .panel--contact::before {
    animation-duration: 42s;
    animation-direction: alternate-reverse;
  }
}

@keyframes panel-drift {
  from {
    transform: scale(1);
  }
  to {
    transform: scale(1.08);
  }
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  position: relative;
  vertical-align: middle;
  min-width: 64px;
  padding: 5px 15px;
  outline: 0;
  background-color: transparent;
  text-decoration: none;
  cursor: pointer;
  -webkit-user-select: none;
  user-select: none;
  font-family: 'Roboto', 'Helvetica', 'Arial', sans-serif;
  font-size: 0.875rem;
  font-weight: 500;
  line-height: 1.75;
  letter-spacing: 0.02857em;
  text-transform: uppercase;
  border-radius: 4px;
  transition: background-color 250ms cubic-bezier(0.4, 0, 0.2, 1),
    color 250ms cubic-bezier(0.4, 0, 0.2, 1),
    box-shadow 250ms cubic-bezier(0.4, 0, 0.2, 1),
    transform 250ms cubic-bezier(0.4, 0, 0.2, 1);

  border: 1px solid #f4f8fb;
  max-height: 36px;
  width: 100px;
  color: #f4f8fb;
  margin: 5px 10px;
}

/* Invert and lift, rather than the near-invisible grey wash this used to do. */
.btn:hover,
.btn:focus-visible {
  background-color: #f4f8fb;
  color: #16191c;
  transform: translateY(-2px);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.4);
}

.btn:active {
  transform: translateY(0);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
}

.btn:focus-visible {
  outline: 2px solid #f4f8fb;
  outline-offset: 3px;
}

/* ==========================================================================
   Icon hover

   The transform goes on the <svg>, not the anchor: the anchors are inline, and
   transforms do not apply to inline boxes. Targeting the SVG keeps the
   surrounding layout exactly as it was.
   ========================================================================== */

.resume__res-icon svg,
.contact__linked-icon svg,
.contact__mail-icon svg {
  transform-origin: center;
  transition: transform 250ms cubic-bezier(0.34, 1.56, 0.64, 1),
    filter 250ms cubic-bezier(0.4, 0, 0.2, 1);
}

.resume__res-icon:hover svg,
.resume__res-icon:focus-visible svg,
.contact__linked-icon:hover svg,
.contact__linked-icon:focus-visible svg,
.contact__mail-icon:hover svg,
.contact__mail-icon:focus-visible svg {
  transform: translateY(-3px) scale(1.14);
  filter: drop-shadow(0 6px 12px rgba(0, 0, 0, 0.5));
}

@media (prefers-reduced-motion: reduce) {
  .btn:hover,
  .btn:focus-visible,
  .btn:active {
    transform: none;
  }

  .resume__res-icon:hover svg,
  .contact__linked-icon:hover svg,
  .contact__mail-icon:hover svg {
    transform: none;
  }
}

/* ==========================================================================
   Header
   ========================================================================== */

.panel--header::before {
  background-image: url('../images/resumeCat.webp');
}

.panel--header {
  grid-template-columns: 25% 35% 40%;
  grid-template-rows: 15% 35% 35% 15%;
}

.header__text {
  grid-column: 3 / 3;
  grid-row: 2 / 2;
  place-self: end center;
  color: #ffffff;
  font-size: 45px;
}

.header__buttons {
  grid-column: 3 / 3;
  grid-row: 3 / 3;
  height: auto;
  width: auto;
  place-self: center;
}

@media (max-width: 38.75em) {
  .panel--header::before {
    background-image: url('../images/resumeCat-sm.webp');
    background-size: cover;
    background-position: 70%;
  }
}

/* ==========================================================================
   About
   ========================================================================== */

.panel--about::before {
  background-image: url('../images/aboutCat.webp');
}

.panel--about {
  grid-template-columns: 25% 35% 40%;
  grid-template-rows: 15% 35% 35% 15%;
}

.about__content {
  grid-column: 2 / 2;
}

.about__title {
  text-align: left;
  width: 75%;
  font-size: 45px;
}

.separator--about {
  --separator-width: 120px;
}

.about__text {
  text-align: left;
  max-width: 75%;
  font-size: 16px;
}

.about__buttons {
  display: flex;
}

@media (max-width: 38.75em) {
  .panel--about {
    grid-template-rows: 5% 35% 35% 25%;
    grid-template-columns: 10% 70% 20%;
  }

  .panel--about::before {
    background-image: url('../images/aboutCat-sm.webp');
    background-size: cover;
    background-position: 60%;
  }
}

/* ==========================================================================
   Resume
   ========================================================================== */

/* This is the one panel where the photo does not span the full width. It is
   held to the left-hand column and dissolved into the page on its right edge,
   so the text column reads against flat black. The falloff is painted as a
   gradient over the photo rather than cut into the mask because the page
   behind it is already black, which keeps this to a single compositing layer
   and avoids mask-composite. */
.panel--resume::before {
  right: 40%;
  background-image: linear-gradient(
      to right,
      rgba(0, 0, 0, 0) 48%,
      rgba(0, 0, 0, 0.18) 64%,
      rgba(0, 0, 0, 0.55) 78%,
      rgba(0, 0, 0, 0.88) 90%,
      rgba(0, 0, 0, 1) 100%
    ),
    url('../images/contactCat.webp');
  background-position: 45% center;
}

.panel--resume {
  grid-template-columns: 65% 35%;
  grid-template-rows: 25% 25% 25% 25%;
}

.resume__content {
  grid-column: 2 / 2;
}

.resume__inner {
  align-self: stretch;
}

.resume__title {
  text-align: left;
  width: 75%;
  font-size: 45px;
  color: #f4f8fb;
}

.separator--resume {
  --separator-width: 80px;
}

/* The four lines here are a list rather than a paragraph, so they sit tighter
   than the shared gap that separates the block from the title above it. */
.resume__text + .resume__text {
  margin-top: 10px;
}

.resume__text {
  text-align: left;
  max-width: 75%;
  font-size: 14px;
}

.resume__res-icon {
  color: inherit;
  margin: 0 var(--icon-gap) 0 -2px;
}

.resume__button {
  margin: var(--control-gap) 0 0 -2px;
}

@media (max-width: 38.75em) {
  /* A phone is portrait, so the desktop left/right split has nowhere to go.
     Stack it instead: the photo spans the full width but is held to the bottom
     of the panel, below the text. The panel's existing top feather does the
     blending, easing the photo up out of the black rather than starting it on
     a hard line. */
  .panel--resume::before {
    right: 0;
    /* The text block is a fixed height (~350px) whatever the viewport, so a
       percentage alone lets the photo climb under it on short screens. The
       pixel floor keeps the top of the photo below the Contact button; the
       percentage takes over on anything taller than roughly 790px. */
    top: max(48%, 380px);
    background-image: url('../images/contactCat-sm.webp');
    background-position: center top;
    background-size: cover;
  }

  /* The desktop layout puts every item in a 35%-wide column, which on a phone
     leaves roughly 150px and pushes the text off the right of the screen.
     Widen the content column the way the About panel already does. */
  .panel--resume {
    grid-template-columns: 8% 84% 8%;
  }

  .resume__title,
  .resume__text {
    width: 100%;
    max-width: 100%;
  }
}

/* ==========================================================================
   Contact
   ========================================================================== */

.panel--contact::before {
  background-image: url('../images/reachOutCat.webp');
}

/* The leading gutter is its own column here, the way the other two panels
   already work, so the shared inset is measured from the same kind of edge. */
.panel--contact {
  grid-template-columns: 11% 34% 55%;
  grid-template-rows: 15% 35% 35% 15%;
}

.contact__inner {
  grid-column: 2 / 2;
}

.contact__title {
  text-align: left;
  width: 75%;
  font-size: 45px;
  color: #f4f8fb;
  text-shadow: 1px 1px 1px #3c5c5e;
}

.separator--contact {
  --separator-width: 110px;
}

.contact__text {
  text-align: left;
  max-width: 75%;
  font-size: 16px;
  text-shadow: 1px 1px 1px #3c5c5e;
}

.contact__icons {
  display: flex;
  align-items: center;
}

/* Icons share one gap and hang off the same left edge as the copy above them;
   the small negative pull offsets the padding built into the glyph's own box. */
.contact__linked-icon,
.contact__mail-icon {
  color: #ffffff;
  margin: 0 var(--icon-gap) 0 -2px;
}

@media (max-width: 38.75em) {
  .panel--contact::before {
    background-image: url('../images/reachOutCat-sm.webp');
    background-size: cover;
    background-position: 40%;
  }

  /* Same wide content column the other two panels use on a phone. */
  .panel--contact {
    grid-template-columns: 8% 84% 8%;
  }

  /* This line is the widest thing in the block, and at the shared measure it
     runs past the black and onto the cat. Hold it to the dark side of the
     frame and let it wrap instead. */
  .contact__text {
    max-width: 42%;
  }
}
