/**
 * Portfolio-specific styles layered on top of the Davies template.
 * Loaded last so these rules win over styles.css.
 */

/* Merriweather replaces the template's Figtree everywhere. The icon font is
   excluded so icomoon glyphs keep rendering, and the template's accent font
   variable is repointed as well. */
:root {
  --font_instrument: var(--font-merriweather), Georgia, serif;
}

body,
body *:not([class^='icon-']):not([class*=' icon-']) {
  font-family: var(--font-merriweather), Georgia, serif !important;
}

/* "ALI HASSAN" is far longer than the template's single-word name, so it is
   sized to sit on one line inside the hero's eight-column block. */
.section-hero-v1 .davies-large {
  font-size: clamp(38px, 8.6vw, 140px);
  line-height: 1.06;
}

@media (min-width: 768px) {
  .section-hero-v1 .davies-large > div {
    white-space: nowrap;
  }
}

/* Role list (FULL-STACK ENGINEER / AUTOMATION ENGINEER / QA ARCHITECT) sized
   up from the template's 12px caption default for more presence in the hero. */
.section-hero-v1 .tf-list.vertical.text-caption {
  font-size: clamp(14px, 1.6vw, 20px);
  line-height: 1.4;
  gap: 8px;
}

/* Text logotype in place of the template's icon-font logo */
.logo-site.logo-text {
  display: inline-block;
  font-size: 28px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.04em;
  color: #ffffff;
  text-decoration: none;
}

.logo-custom .logo-site-sv.logo-text {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 26px;
  font-weight: 700;
  letter-spacing: -0.04em;
  color: #ffffff;
}

/* Still image standing in for the template's hero background video.
   It fades in on load and dissolves into the black page at top and bottom. */
.section-hero-v1 .bg-video.bg-still img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  display: block;
  opacity: 0.5;
  -webkit-mask-image: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, #000 22%, #000 62%, rgba(0, 0, 0, 0) 100%);
  mask-image: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, #000 22%, #000 62%, rgba(0, 0, 0, 0) 100%);
  animation: heroStillFadeIn 1.6s ease-out both;
}

@keyframes heroStillFadeIn {
  from {
    opacity: 0;
    transform: scale(1.06);
  }
  to {
    opacity: 0.5;
    transform: scale(1);
  }
}

@media (prefers-reduced-motion: reduce) {
  .section-hero-v1 .bg-video.bg-still img {
    animation: none;
  }
}

/* The template's hue-blend overlay assumes an opaque video behind it; over a
   faded still it floods the section with the accent colour, so the still
   relies on the plain black overlays only. */
.section-hero-v1 .bg-video.bg-still .video-overlay {
  display: none;
}

.section-hero-v1 .bg-video.bg-still .video-overlay-2 {
  opacity: 0.3;
}

/* Experience marks (E1, E2, E3) replace the template's S1/S2/S3 artwork */
.section-process .wg-process .exp-mark {
  display: block;
  font-size: 64px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.03em;
  color: #ffffff;
  white-space: nowrap;
}

/* Role period line inside the experience cards */
.section-process .wg-process .content .title + .text-caption {
  margin-top: 8px;
  margin-bottom: 0;
}

/* The fanned stack paints in DOM order, so the oldest role (E1) landed on top
   and the current one sat buried at the back with only a sliver showing.
   Reversing the z-order deals the deck the other way: the newest role fully
   visible in front, down to E1 furthest back. Hover still lifts any card. */
@media (min-width: 1200px) {
  .section-process .swiper-slide:nth-child(1) {
    z-index: 4;
  }

  .section-process .swiper-slide:nth-child(2) {
    z-index: 3;
  }

  .section-process .swiper-slide:nth-child(3) {
    z-index: 2;
  }

  /* The template only staggers three cards, so a fourth drops back to the
     generic -5% offset and sits flat against the top of the row. These
     continue the cascade: one more step down and one more step left. */
  .section-process .swiper-slide:nth-child(4) {
    margin-top: 216px;
    left: -15%;
    z-index: 1;
  }

  .section-process .swiper-slide:hover {
    z-index: 5;
  }
}

/* Above 1440 the cards stop flexing and lock to 509px, so the row runs wider
   than the container and the negative offsets are what pull it back into
   frame. The template's -139px step is sized for exactly three cards; a fourth
   adds another 509px of overrun, so each step has to grow to a third of the
   total excess. Percentages resolve against the wrapper, so this keeps the fan
   flush with the container instead of spilling into a horizontal scrollbar. */
@media (min-width: 1440px) {
  .section-process .swiper-slide:nth-child(2) {
    left: -21%;
  }

  .section-process .swiper-slide:nth-child(3) {
    left: -42%;
  }

  .section-process .swiper-slide:nth-child(4) {
    left: -63%;
  }
}

/* Selected Works image frame. Slide height came from each image's own aspect
   ratio, so mixed sources made the slider jump; a fixed 16:9 frame keeps every
   slide the same size. */
.slick-for .image {
  aspect-ratio: 16 / 9;
  overflow: hidden;
}

.slick-for .image img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Logo artwork rather than a screenshot: centred on white at its native size,
   so it is never cropped or upscaled past its real resolution. */
.slick-for .image.is-contain {
  background-color: #ffffff;
  display: flex;
  align-items: center;
  justify-content: center;
}

.slick-for .image.is-contain img {
  width: auto;
  height: auto;
  max-width: 45%;
  max-height: 60%;
  object-fit: contain;
}

/* Selected Works name list.
   The template throws non-active names 45deg back in 3D, which crowds the
   column and clips long titles. This flattens the scroll and instead separates
   the active project by size and opacity alone. */
.slick-nav {
  perspective: none;
}

.slick-nav .slick-slide,
.slick-nav .slick-slide:not(.slick-current),
.slick-nav .slick-current + .slick-slide,
.slick-nav .slick-current ~ .slick-slide:last-child {
  transform: none;
  opacity: 1;
  padding: 6px 0;
}

.slick-nav .slick-slide .text-slide,
.slick-nav .slick-slide .work-company {
  transform: scale(0.62);
  transform-origin: left center;
  opacity: 0.3;
  transition: transform 0.55s ease, opacity 0.55s ease, color 0.55s ease;
}

/* Names must stay on one line: slick's vertical track assumes every slide is
   the same height, and a wrapped name made its slide taller than the list,
   which clipped it and pushed its neighbours out of place. */
.slick-nav .slick-slide .text-slide {
  font-size: clamp(22px, 2.6vw, 44px);
  line-height: 1.15;
  margin-bottom: 2px;
  white-space: nowrap;
}

.slick-nav .slick-slide .work-company {
  color: var(--white-64);
  margin-bottom: 0;
}

/* The highlighted project grows to full size and full contrast */
.slick-nav .slick-current .text-slide,
.slick-nav .slick-current .work-company {
  transform: scale(1);
  opacity: 1;
}

/* Rounded corners on every framed image. Full-bleed artwork — the hero still,
   the section backdrops, the About portrait (whose edges already dissolve into
   black) — stays square so it still meets the edges of its section. */
.slick-for .image,
.wg-service-2 .main-image .image,
.wg-service-2 .image-2 {
  border-radius: 16px;
}

/* Safari repaints the radius away during the slick transform unless the frame
   gets its own layer. */
.slick-for .image {
  isolation: isolate;
}

.wg-service-2 .image-2 {
  overflow: hidden;
}

@media (max-width: 767px) {
  .slick-for .image,
  .wg-service-2 .main-image .image,
  .wg-service-2 .image-2 {
    border-radius: 12px;
  }
}

/* Offcanvas menu links. The template sizes them off viewport WIDTH — up to a
   96px face in a 108px line box — so on a wide but short screen the five links
   plus the header and footer ran past the bottom and the list fell back to its
   own scrollbar. Driving the type off viewport HEIGHT instead keeps the whole
   menu on one screen at any window size. */
.offcanvas-menu .nav-ul-mb .mb-menu-link {
  font-size: clamp(30px, 7.4vh, 68px);
  line-height: clamp(44px, 10.6vh, 96px);
}

/* Short windows also give back some of the panel's own padding */
@media (max-height: 700px) {
  .offcanvas-menu .offcanvas-content {
    padding-top: 24px;
    padding-bottom: 24px;
  }

  .offcanvas-menu .offcanvas-content_wrapin {
    gap: 12px;
  }
}

/* Mobile menu scrim: the template's 56% black with a 4px blur leaves the page
   behind the links fully legible, so it is darkened and blurred harder. */
.offcanvas-menu {
  background-color: rgba(0, 0, 0, 0.93);
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
}

/* Portrait photo standing in for the About Me video. The frame is taller than
   the square source, so cover crops the sides and keeps the face in frame. */
.davies-video.is-portrait img.video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 30%;
  display: block;
}

/* What I Do card imagery. The template's tall portrait frame cropped away most
   of each landscape source and cut the caption off the bottom of the two
   illustrations. A 4:3 frame matches those two exactly, so they fill it edge to
   edge with nothing lost; the automation photo is 16:9 and gives up a little
   width, which costs it nothing since it carries no caption. */
.wg-service-2 .main-image .image {
  aspect-ratio: 4 / 3;
  overflow: hidden;
}

.wg-service-2 .main-image .image img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

/* The next-pillar preview morphs into the card image, so it matches its frame */
.wg-service-2 .image-2 {
  aspect-ratio: 4 / 3;
}

.wg-service-2 .image-2 img {
  object-fit: cover;
}

.section-service-2 .s-header h2 {
  font-size: clamp(30px, 3.6vw, 52px);
  line-height: 1.1;
}

/* The pillar-to-pillar arrow. The template's smoked-glass disc reads as
   decoration against the dark card, so it is inverted to a bright white puck —
   the one light source in the section — and dressed to read as a control:
   a slow beacon ring so it invites a click before anyone hovers it, an accent
   glow that blooms on hover, and the arrow itself swapping out for a fresh one
   in the direction it will travel. */
.wg-service-2 .main-image .action {
  background: radial-gradient(120% 120% at 30% 22%, #ffffff 0%, #ffffff 54%, #e9fff0 100%);
  color: #0b0b0b;
  border: 0;
  backdrop-filter: none;
  isolation: isolate;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.9), 0 10px 30px -10px rgba(0, 0, 0, 0.8),
    0 0 26px -6px rgba(7, 196, 44, 0.35);
  transition: transform 0.4s cubic-bezier(0.22, 0.61, 0.36, 1), box-shadow 0.4s ease;
}

/* Beacon: a ring that swells out from behind the disc on a loop. z-index -1
   inside the button's own stacking context keeps it under the white face, so
   it reads as light escaping from behind rather than a ring drawn on top. */
.wg-service-2 .main-image .action::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.55);
  pointer-events: none;
  animation: actionBeacon 2.8s cubic-bezier(0.16, 0.8, 0.4, 1) infinite;
}

@keyframes actionBeacon {
  0% {
    transform: scale(1);
    opacity: 0.6;
  }
  70%,
  100% {
    transform: scale(1.55);
    opacity: 0;
  }
}

.wg-service-2 .main-image .action:hover {
  /* translate(50%, -50%) is the template's centring on the frame edge and has
     to be repeated here, or hovering would knock the button out of place. */
  transform: translate(50%, -50%) scale(1.08);
  box-shadow: 0 0 0 1px #ffffff, 0 0 0 10px rgba(7, 196, 44, 0.16),
    0 0 46px -4px rgba(7, 196, 44, 0.55), 0 20px 46px -12px rgba(0, 0, 0, 0.9);
}

.wg-service-2 .main-image .action:active {
  transform: translate(50%, -50%) scale(0.97);
}

/* Arrow swap. The glyph lives in the icon's ::before, so ::after carries a
   second copy of the same codepoint waiting off the opposite edge: on hover the
   first slides out and the second takes its place, clipped by the icon box. */
.wg-service-2 .main-image .action .icon {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  /* Taller than 1em on purpose: at line-height 1 the box is exactly the em
     square and `overflow: hidden` shaves the glyph's ascender. The extra height
     costs nothing, since the clip that matters for the swap is horizontal. */
  line-height: 1.35;
}

.wg-service-2 .main-image .action .icon::before,
.wg-service-2 .main-image .action .icon::after {
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform 0.45s cubic-bezier(0.65, 0, 0.35, 1);
}

.wg-service-2 .main-image .action .icon::after {
  position: absolute;
  inset: 0;
}

.wg-service-2 .main-image .action .icon-arrow-long-right::after {
  content: "\e903";
  transform: translateX(-120%);
}

.wg-service-2 .main-image .action:hover .icon-arrow-long-right::before {
  transform: translateX(120%);
}

.wg-service-2 .main-image .action .icon-arrow-long-left::after {
  content: "\e902";
  transform: translateX(120%);
}

.wg-service-2 .main-image .action:hover .icon-arrow-long-left::before {
  transform: translateX(-120%);
}

.wg-service-2 .main-image .action:hover .icon::after {
  transform: translateX(0);
}

.wg-service-2 .main-image .action:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 6px;
}

@media (prefers-reduced-motion: reduce) {
  .wg-service-2 .main-image .action,
  .wg-service-2 .main-image .action .icon::before,
  .wg-service-2 .main-image .action .icon::after {
    transition: none;
  }

  .wg-service-2 .main-image .action::before {
    animation: none;
  }
}

/* The section is pinned while the three pillars cross-fade, so on desktop every
   card has to fit one viewport exactly — anything taller is unreachable, since
   the pin holds the section still. Spacing and type are therefore driven by vh
   rather than fixed px, and the whole block is centred in the remaining room. */
@media (min-width: 1200px) {
  .section-service-2 {
    min-height: 100svh;
    padding-top: clamp(20px, 5vh, 64px);
    padding-bottom: clamp(20px, 5vh, 64px);
  }

  .section-service-2 .s-header {
    margin-bottom: clamp(12px, 3vh, 40px);
  }

  .wg-service-2 {
    align-items: center;
    gap: clamp(40px, 5.5vw, 121px);
  }

  .wg-service-2 .main-image {
    max-width: clamp(260px, 26vw, 424px);
  }

  .wg-service-2 .main-image .image {
    max-height: 46vh;
  }

  .wg-service-2 .center .title {
    font-size: clamp(20px, 3vh, 30px);
    margin-bottom: clamp(8px, 1.6vh, 16px);
  }

  .wg-service-2 .center .desc {
    font-size: clamp(14px, 1.7vh, 16px);
    line-height: 1.55;
    margin-bottom: clamp(12px, 2.4vh, 26px);
  }

  .wg-service-2 .center .br-line {
    margin-bottom: clamp(12px, 2.2vh, 22px);
  }

  .wg-service-2 .center .tf-list {
    margin-bottom: clamp(12px, 2.4vh, 26px);
    gap: clamp(6px, 1.4vh, 16px);
  }

  .wg-service-2 .center .tf-list li {
    font-size: clamp(14px, 1.7vh, 16px);
    line-height: 1.5;
  }
}

/* Below the pin threshold the cards stack and scroll normally, so they only
   need the tightened rhythm, not the viewport cap. */
@media (max-width: 1199px) {
  .section-service-2 {
    padding-top: 64px;
    padding-bottom: 64px;
  }

  .section-service-2 .s-header {
    margin-bottom: 24px;
  }

  .wg-service-2 .center .title {
    font-size: clamp(20px, 1.9vw, 26px);
    margin-bottom: 10px;
  }

  .wg-service-2 .center .desc {
    font-size: 15px;
    line-height: 1.55;
    margin-bottom: 24px;
  }

  .wg-service-2 .center .br-line {
    margin-bottom: 20px;
  }

  .wg-service-2 .center .tf-list {
    margin-bottom: 24px;
  }

  .wg-service-2 .center .tf-list li {
    font-size: 15px;
  }
}

/* Skills grid */
.section-skills .skill-grid {
  row-gap: 48px;
}

.section-skills .skill-group .mini-title {
  margin-bottom: 12px;
}

.section-skills .skill-group .br-line {
  margin-bottom: 20px;
}

.section-skills .skill-group .tf-list li {
  margin-bottom: 10px;
}

/* Nine skill groups stacked one per row ran the section past 3000px on a
   phone. The entries are short labels, so below the tablet breakpoint each
   group's list flows into two columns — same content, roughly half the
   scrolling. Flex has to give way to block for CSS columns to apply. */
@media (max-width: 767px) {
  .section-skills .skill-group .tf-list.vertical {
    display: block;
    columns: 2;
    column-gap: 20px;
  }

  .section-skills .skill-group .tf-list.vertical li {
    break-inside: avoid;
  }

  .section-skills .skill-grid {
    row-gap: 36px;
  }
}

/* Education / languages lists reuse the award item styling */
.section-education .education-list {
  list-style: none;
  padding: 0;
  margin: 0;
}

.section-education .education-list li {
  padding: 20px 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.12);
}

.section-education .education-list li:first-child {
  border-top: 1px solid rgba(255, 255, 255, 0.12);
}

.section-education .education-list .award-item {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 4px;
}

.section-education .col-left {
  margin-bottom: 48px;
}

/* The year and the language level sit opposite the title in a space-between
   row. On a phone the row gets tight enough that "2022 / 2026" broke across
   two lines and pushed the item to double height. */
.section-education .award-item span:last-child {
  white-space: nowrap;
}

/* Tech stack slot holds a discipline label rather than a percentage */
.section-tech-stack .tech_text h6 {
  white-space: nowrap;
  opacity: 0.64;
}

/* Contact details block beside the submit button */
.form-cta .contact-details p {
  margin-bottom: 4px;
}

.form-cta .contact-details p:last-child {
  margin-bottom: 0;
}

/* Contact fields are 28px tall — a fine target for a mouse, well under the
   ~44px a thumb needs. Below 576px the template already drops the floating
   label for a stacked one, so there is no overlay to disturb by growing them. */
@media (max-width: 575px) {
  .form-cta .tf-input,
  .form-cta .nice-select {
    min-height: 44px;
    padding-bottom: 10px;
  }
}

/* The contact select is driven by React rather than the nice-select plugin */
.form-cta .nice-select .list {
  display: none;
}

.form-cta .nice-select.open .list {
  display: block;
}
