diff --git a/.github/workflows/jekyll-gh-pages.yml b/.github/workflows/jekyll-gh-pages.yml new file mode 100644 index 0000000..67be9b0 --- /dev/null +++ b/.github/workflows/jekyll-gh-pages.yml @@ -0,0 +1,51 @@ +# Sample workflow for building and deploying a Jekyll site to GitHub Pages +name: Deploy Jekyll with GitHub Pages dependencies preinstalled + +on: + # Runs on pushes targeting the default branch + push: + branches: ["main"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Build job + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Pages + uses: actions/configure-pages@v5 + - name: Build with Jekyll + uses: actions/jekyll-build-pages@v1 + with: + source: ./ + destination: ./_site + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + + # Deployment job + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v5 diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml new file mode 100644 index 0000000..460f782 --- /dev/null +++ b/.github/workflows/static.yml @@ -0,0 +1,43 @@ +# Simple workflow for deploying static content to GitHub Pages +name: Deploy static content to Pages + +on: + # Runs on pushes targeting the default branch + push: + branches: ["main"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Single deploy job since we're just deploying + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Pages + uses: actions/configure-pages@v5 + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + # Upload entire repository + path: '.' + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v5 diff --git a/CNAME b/CNAME new file mode 100644 index 0000000..6a1d95d --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +hafizurrahman.tech \ No newline at end of file diff --git a/README.md b/README.md index 43f1a0e..3832681 100644 --- a/README.md +++ b/README.md @@ -1,76 +1 @@ -[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new?hide_repo_select=true&ref=main&repo=526682619) - -# Use GitHub Copilot to write JavaScript - -Explore how you can modify a JavaScript repository using code suggestions from GitHub Copilot to modify and customize a web application. By working with this repository, you'll quickly get hands-on with a JavaScript web app for a portfolio site. - -## Requirements - -1. Enable your [GitHub Copilot service](https://github.com/github-copilot/signup) -1. Open [this repository with Codespaces](https://codespaces.new/MicrosoftDocs/mslearn-copilot-codespaces-javascript?quickstart=1) - -## 💪🏽 Exercise - -In this template portfolio, we have a React based web application ready for you to easily customize and deploy using only your web browser. - - -### 🛠 Step 1: Customize the web app - -Customize the portfolio with your own links. Go to `src/App.jsx` and update the `siteProps` with your information. The `siteProps` variable is a JavaScript object that holds key value pairs used to customize the site, it should look like this: - -```javascript -const siteProps = { - name: "Alexandrie Grenier", - title: "Web Designer & Content Creator", - email: "alex@example.com", - gitHub: "microsoft", - instagram: "microsoft", - linkedIn: "satyanadella", - medium: "", - twitter: "microsoft", - youTube: "Code", -}; -``` - -### 🔎 Step 2: Animate the social media icons with a prompt - -An animation can make the social media section more eye-catching. Ask Copilot’s help to animate the icons. Write the following prompt in the `src/styles.css` file: - -```css -/* add an amazing animation to the social icons */ -``` - -The suggestion from Copilot should look similar to the following: - -```css -img.socialIcon:hover { - animation: bounce 0.5s; - animation-iteration-count: infinite; -} - -@keyframes bounce { - 0% { - transform: scale(1); - } - 50% { - transform: scale(1.2); - } - 100% { - transform: scale(1); - } -} -``` - -### 🚀 Step 3: Find out how to run the app -Open GitHub Copilot chat by clicking on the chat icon on the left side bar and use the input section to ask the following: - -``` -@workspace I want to understand how can I run this React application -``` - -The prompts uses `@workspace` which is a special feature of GitHub Copilot chat that allows you to include more context for a more complete answer. Try out other queries using the chat panel for a more interactive workflow. - -### Conclusion -Your site should already be running in your Codespace, and the change will reload onto the page automatically. To see them, hover over one of your social media icons in the footer to see the magic! - -Congratulations, through the exercise, you have use GitHub Copilot to generate code and also done it in an interactive and fun way! You can use GitHub Copilot to not only generate code, but write documentation, test your applications and more. +Hafizur_Rahmans_Portfolio diff --git a/src/App.jsx b/src/App.jsx index 4fbc89d..930075f 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -24,19 +24,17 @@ import "./styles.css"; * If you don't have one of the social sites listed, leave it as an empty string. */ const siteProps = { - name: "Alexandrie Grenier", - title: "Web Designer & Content Creator", - email: "alex@example.com", - gitHub: "microsoft", - instagram: "microsoft", - linkedIn: "satyanadella", - medium: "", - twitter: "microsoft", - youTube: "Code", + name: "Hafizur Rahman Provat", + title: "Software Engineering Student", + email: "vatpro8@gmail.com", + phone: "01763676326", + address: "Ashulia, Savar, 1341 Dhaka", + website: "https://neo-crisp-4dca81.netlify.app", + linkedIn: "https://linkedin.com/in/hafizur-rahman-0a404221a", }; -const primaryColor = "#4E567E"; -const secondaryColor = "#D2F1E4"; +const primaryColor = "#173f5f"; +const secondaryColor = "#f6c85f"; const App = () => { return ( diff --git a/src/Components/About.jsx b/src/Components/About.jsx index 9eaad4a..15cd17b 100644 --- a/src/Components/About.jsx +++ b/src/Components/About.jsx @@ -6,37 +6,29 @@ import React from "react"; -/** - * About background image - * - * Below is a sample image. Upload the image of your choice into the "images" - * directory and import here for use. Then, set imageAltText to string that - * represents what you see in that image. - * - * Need an image? Check out https://unsplash.com to download a image you - * freely use on your site. - */ -import image from "../images/motion-background.jpg"; - -const imageAltText = "purple and blue abstract background"; - /** * Sort description that expands on your title on the Home component. */ const description = - "I'm a UI/UX student studying at Barnett Technical University. I enjoy creating unique and simplistic user interfaces in creative ways."; + "Enthusiastic and dedicated Software Engineering student from Dhaka, eager to apply academic knowledge and practical skills in a dynamic software development internship."; /** * List of some of skills or technologies you work on, are learning, * passionate about, or enjoy, */ const skillsList = [ - "Web design", - "User experience", - "Inclusive design", - "Focus group testing", - "Mobile user interfaces", - "Graphic design", + "Java", + "Python", + "C++", + "JavaScript", + "Software development", + "Problem solving", + "Version control (Git)", + "MySQL", + "PHP / Laravel", + "DevOps", + "System architecture", + "Project architecture", ]; /** @@ -45,39 +37,60 @@ const skillsList = [ * about you on a professional level. */ const detailOrQuote = - "I am passionate about solving problems in new creative ways to drive innovation. By leveraging my UI/UX experience I continually look for new and better ways to make tech accessible by all."; + "I bring a foundation in data structures, algorithms, software design, RESTful APIs, and unit testing, with a focus on practical problem solving and collaboration."; + +const education = "B.Sc. in Software Engineering, Daffodil International University, Dhaka / Jun 2023 - Aug 2027"; + +const certifications = [ + "GitHub Copilot Fundamentals from Microsoft Learn / Sep 2026", + "Introduction to Open Source / Present", + "Microsoft Azure Products: AZ-900 and AZ-204", + "Web Development with Python and JavaScript / Feb 2024", + "Microsoft Excel for Data Analysis / 2026", + "GitHub Copilot Foundation for DevOps, System Architecture and Project Architecture / 2026", +]; + +const achievements = [ + "Deployed a university web application to improve local business operations in Dhaka.", + "Optimized academic codebases to reduce loading times by 30%.", + "Led RESTful API integration for a campus event management system, improving engagement by 25%.", + "Implemented JUnit and PyTest unit testing, reducing bugs by 40%.", +]; const About = () => { return ( -
- {imageAltText} -
-

About Myself

+
+
+

Profile / capabilities

+

Curious by nature. Practical by design.

{description}

-
-
    +

    {detailOrQuote}

    +
+
+

Technical toolkit

+
    {skillsList.map((skill) => (
  • {skill}
  • ))}
-
-

{detailOrQuote}

+
+
+
+

Education

+

{education}

+
+
+

Certifications

+
    + {certifications.map((certificate) =>
  • {certificate}
  • )} +
+
+
+

Achievements

+
    + {achievements.map((achievement) =>
  • {achievement}
  • )} +
+
); diff --git a/src/Components/Footer.jsx b/src/Components/Footer.jsx index d765179..8a250b4 100644 --- a/src/Components/Footer.jsx +++ b/src/Components/Footer.jsx @@ -10,15 +10,6 @@ import React from "react"; import PropTypes from "prop-types"; -import devDotToIcon from "../images/socials/devdotto.svg"; -import envelopeIcon from "../images/socials/envelope.svg"; -import gitHubIcon from "../images/socials/github.svg"; -import instagramIcon from "../images/socials/instagram.svg"; -import linkedInIcon from "../images/socials/linkedin.svg"; -import mediumIcon from "../images/socials/medium.svg"; -import twitterIcon from "../images/socials/twitter.svg"; -import youTubeIcon from "../images/socials/youtube.svg"; - /** * 💡 Learning resources * @@ -28,18 +19,50 @@ import youTubeIcon from "../images/socials/youtube.svg"; const Footer = (props) => { const { - devDotTo, email, - gitHub, - instagram, linkedIn, - medium, name, primaryColor, - twitter, - youTube, + phone, + address, + website, } = props; + const workAreas = [ + { + label: "AWS Cloud", + image: "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/amazonwebservices/amazonwebservices-original-wordmark.svg", + }, + { + label: "Azure DevOps", + image: "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/azure/azure-original.svg", + }, + { + label: "Docker", + image: "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/docker/docker-original.svg", + }, + { + label: "Cloud architecture", + image: "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/kubernetes/kubernetes-plain.svg", + }, + { + label: "Android", + image: "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/android/android-original.svg", + }, + { + label: "Open source", + image: "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/github/github-original.svg", + }, + { + label: "Developer tools", + image: "https://cdn.jsdelivr.net/gh/devicons/devicon/icons/vscode/vscode-original.svg", + }, + { + label: "MCP protocol", + image: "https://cdn.simpleicons.org/modelcontextprotocol/ffffff", + }, + ]; + return ( ); }; diff --git a/src/Components/Home.jsx b/src/Components/Home.jsx index a6a8721..8a632ed 100644 --- a/src/Components/Home.jsx +++ b/src/Components/Home.jsx @@ -1,40 +1,30 @@ /** * Home component * - * The section at the top of the page to display image of your - * choice, name and title that describes your career focus. + * The section at the top of the page that introduces the portfolio owner. */ import React from "react"; -import arrowSvg from "../images/down-arrow.svg"; import PropTypes from "prop-types"; - -/** - * Home background image - * - * Below is a sample image. Upload the image of your choice into the "images" - * directory and import here for use. Then, set imageAltText to string that - * represents what you see in that image. - * - * - * Need an image? Check out https://unsplash.com to download a photo you - * freely use on your site. - */ -import image from "../images/woman-with-tablet.jpg"; - -const imageAltText = "Adult female in office setting leaning against a glass wall while holding a platinum Microsoft Surface Pro 7 in tablet mode preparing to write with Microsoft Surface Pen"; +import heroImage from "../Image/Vibe.jpeg"; const Home = ({ name, title }) => { return (
- -
-

{name}

-

{title}

-
-
- {imageAltText} +
+
+

Software engineering / Dhaka

+

{name}

+

{title}

+

+ Building practical software with Java, Python, JavaScript, and cloud technologies. +

+
+
+ Scroll to explore
); }; diff --git a/src/Components/Portfolio.jsx b/src/Components/Portfolio.jsx index ab1c924..72600e8 100644 --- a/src/Components/Portfolio.jsx +++ b/src/Components/Portfolio.jsx @@ -9,19 +9,9 @@ */ import React from "react"; - -/** - * Desk image - * - * Below is a sample desk image. Feel free to update this to an image of your choice, - * updating below imageAltText to string that represents what you see in that image. - * - * Need an image? Check out https://unsplash.com to download a photo you - * freely use on your site. - */ -import image from "../images/design-desk.jpeg"; - -const imageAltText = "desktop with books and laptop"; +import vibeImage from "../Image/Vibe.jpeg"; +import hotelImage from "../Image/Hotel-motel.jpeg"; +import nightImage from "../Image/night1.jpeg"; /** * Project list @@ -31,53 +21,40 @@ const imageAltText = "desktop with books and laptop"; */ const projectList = [ { - title: "10 Things To Know About Azure Static Web Apps 🎉", - description: - "Collaboration to create a beginner friendly article to help explain Azure Static Web Apps and tooling to get started.", - url: "https://dev.to/azure/10-things-to-know-about-azure-static-web-apps-3n4i", - }, - { - title: "Web Development for Beginners", + title: "Colorant and color mixing software", description: - "Contributed sketch note imagery to accompany each lesson. These help provide visual representation of what is being taught.", - url: "https://github.com/microsoft/web-dev-for-beginners", - }, - { - title: "My Resume Site", - description: - "Created from Microsoft's resume workshop and deployed to GitHub pages. Includes my experience and design abilities.", - url: "https://github.com/microsoft/workshop-library/tree/main/full/build-resume-website", - }, - { - title: "GitHub Codespaces and github.dev", - description: - "Video interview to explain when to use GitHub.dev versus GitHub Codespaces, and how best to use each tool.", - url: "https://www.youtube.com/watch?v=c3hHhRME_XI", + "Product development work for Berger Paints, inspecting the software process from price details through color grading and dispensing.", }, ]; const Portfolio = () => { return ( -
-

Portfolio

-
-
- {imageAltText} +
+
+
+

Selected work

+

From coursework to useful software.

-
- {projectList.map((project) => ( -
- -

{project.title}

-
-

{project.description}

-
- ))} +

Visual studies from my work and learning journey.

+
+
+
+

01 / Product development

+

{projectList[0].title}

+

{projectList[0].description}

+

Berger Paints / April 2024

+ Project visual for colorant and color mixing software +
+
+
+ Selected portfolio visual titled Hotel-motel +
Selected work / 02
+
+
+ Selected portfolio visual titled night1 +
Selected work / 03
+
); diff --git a/src/Document/Hafizur_Rahman_Provat_Resume_Updated-1.pdf b/src/Document/Hafizur_Rahman_Provat_Resume_Updated-1.pdf new file mode 100644 index 0000000..f9b8b94 Binary files /dev/null and b/src/Document/Hafizur_Rahman_Provat_Resume_Updated-1.pdf differ diff --git a/src/Image/Hotel-motel.jpeg b/src/Image/Hotel-motel.jpeg new file mode 100644 index 0000000..64ea58e Binary files /dev/null and b/src/Image/Hotel-motel.jpeg differ diff --git a/src/Image/University.jpeg b/src/Image/University.jpeg new file mode 100644 index 0000000..73cd79a Binary files /dev/null and b/src/Image/University.jpeg differ diff --git a/src/Image/Vibe.jpeg b/src/Image/Vibe.jpeg new file mode 100644 index 0000000..514f648 Binary files /dev/null and b/src/Image/Vibe.jpeg differ diff --git a/src/Image/night1.jpeg b/src/Image/night1.jpeg new file mode 100644 index 0000000..3655d36 Binary files /dev/null and b/src/Image/night1.jpeg differ diff --git a/src/images/design-desk.jpeg b/src/images/design-desk.jpeg deleted file mode 100644 index 3b8449e..0000000 Binary files a/src/images/design-desk.jpeg and /dev/null differ diff --git a/src/images/down-arrow.svg b/src/images/down-arrow.svg deleted file mode 100644 index e4848db..0000000 --- a/src/images/down-arrow.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/images/motion-background.jpg b/src/images/motion-background.jpg deleted file mode 100644 index d2fc9e7..0000000 Binary files a/src/images/motion-background.jpg and /dev/null differ diff --git a/src/images/socials/devdotto.svg b/src/images/socials/devdotto.svg deleted file mode 100644 index 781626f..0000000 --- a/src/images/socials/devdotto.svg +++ /dev/null @@ -1 +0,0 @@ -dev.to \ No newline at end of file diff --git a/src/images/socials/envelope.svg b/src/images/socials/envelope.svg deleted file mode 100644 index 45703ef..0000000 --- a/src/images/socials/envelope.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/images/socials/github.svg b/src/images/socials/github.svg deleted file mode 100644 index a44134c..0000000 --- a/src/images/socials/github.svg +++ /dev/null @@ -1 +0,0 @@ -GitHub \ No newline at end of file diff --git a/src/images/socials/instagram.svg b/src/images/socials/instagram.svg deleted file mode 100644 index fdc98c9..0000000 --- a/src/images/socials/instagram.svg +++ /dev/null @@ -1 +0,0 @@ -Instagram \ No newline at end of file diff --git a/src/images/socials/linkedin.svg b/src/images/socials/linkedin.svg deleted file mode 100644 index 9d6f855..0000000 --- a/src/images/socials/linkedin.svg +++ /dev/null @@ -1 +0,0 @@ -LinkedIn \ No newline at end of file diff --git a/src/images/socials/medium.svg b/src/images/socials/medium.svg deleted file mode 100644 index f66afa8..0000000 --- a/src/images/socials/medium.svg +++ /dev/null @@ -1 +0,0 @@ -Medium \ No newline at end of file diff --git a/src/images/socials/microsoft.svg b/src/images/socials/microsoft.svg deleted file mode 100644 index 1169efa..0000000 --- a/src/images/socials/microsoft.svg +++ /dev/null @@ -1 +0,0 @@ -Microsoft \ No newline at end of file diff --git a/src/images/socials/twitter.svg b/src/images/socials/twitter.svg deleted file mode 100644 index 98e10da..0000000 --- a/src/images/socials/twitter.svg +++ /dev/null @@ -1 +0,0 @@ -Twitter \ No newline at end of file diff --git a/src/images/socials/youtube.svg b/src/images/socials/youtube.svg deleted file mode 100644 index b52d18c..0000000 --- a/src/images/socials/youtube.svg +++ /dev/null @@ -1 +0,0 @@ -YouTube \ No newline at end of file diff --git a/src/images/woman-with-tablet.jpg b/src/images/woman-with-tablet.jpg deleted file mode 100644 index 2c8a39f..0000000 Binary files a/src/images/woman-with-tablet.jpg and /dev/null differ diff --git a/src/index.js b/src/index.js index 6fb80ae..b98b8e1 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,5 @@ /** - * Entry point of application, where App is rendered within the div with the id of "app" + * Entry point of application, where App is rendered within the div with the id of "app" */ import React from "react"; diff --git a/src/styles.css b/src/styles.css index 999a174..b2a3e1c 100644 --- a/src/styles.css +++ b/src/styles.css @@ -119,7 +119,681 @@ a:hover { text-align: center; } +#footer > div:first-child > a { + align-items: center; + display: flex; + height: 3.5rem; + justify-content: center; + position: relative; + width: 3.5rem; +} + +#footer > div:first-child > a::before, +#footer > div:first-child > a::after { + border: 1px solid rgba(255, 255, 255, 0.65); + border-radius: 50%; + content: ""; + inset: 0; + opacity: 0; + position: absolute; + transform: scale(0.45); +} + +#footer > div:first-child > a::after { + border-color: rgba(255, 255, 255, 0.25); + transform: scale(0.8); +} + img.socialIcon { height: 30px; + position: relative; + transition: transform 350ms cubic-bezier(0.2, 0.8, 0.2, 1), filter 350ms ease; width: 30px; + z-index: 1; +} + +#footer > div:first-child > a:hover, +#footer > div:first-child > a:focus-visible { + color: inherit; + transform: translateY(-8px) rotate(-4deg); +} + +#footer > div:first-child > a:hover .socialIcon, +#footer > div:first-child > a:focus-visible .socialIcon { + filter: drop-shadow(0 8px 6px rgba(0, 0, 0, 0.2)); + transform: scale(1.2) rotate(8deg); +} + +#footer > div:first-child > a:hover::before, +#footer > div:first-child > a:focus-visible::before { + animation: socialOrbit 900ms cubic-bezier(0.2, 0.8, 0.2, 1) forwards; +} + +#footer > div:first-child > a:hover::after, +#footer > div:first-child > a:focus-visible::after { + animation: socialOrbit 900ms 100ms cubic-bezier(0.2, 0.8, 0.2, 1) forwards; +} + +#footer > div:first-child > a:focus-visible { + outline: 2px solid white; + outline-offset: 5px; +} + +.workAreas { + display: grid; + gap: 0.75rem; + grid-template-columns: repeat(4, minmax(0, 1fr)); + max-width: 760px; + width: 100%; +} + +.workArea { + align-items: center; + border: 1px solid rgba(250, 248, 243, 0.2); + display: flex; + gap: 0.75rem; + min-height: 4.5rem; + padding: 0.85rem 1rem; + transition: background-color 180ms ease, border-color 180ms ease, transform 180ms ease; +} + +.workArea:hover { + background: rgba(250, 248, 243, 0.08); + border-color: rgba(246, 200, 95, 0.7); + transform: translateY(-3px); +} + +.workArea img { + filter: grayscale(1) brightness(0) invert(1); + height: 2rem; + object-fit: contain; + width: 2rem; +} + +.workArea:first-child img { + width: 3.25rem; +} + +.workArea span { + color: var(--paper); + font-size: 0.72rem; + font-weight: 800; + letter-spacing: 0.06em; + line-height: 1.2; + text-transform: uppercase; +} + +.contactLinks { + display: flex; + flex-wrap: wrap; + gap: 0.75rem 1.5rem; + justify-content: center; +} + +.contactLinks a { + color: var(--paper); + font-size: 0.82rem; +} + +.contactLinks a:hover, +.contactLinks a:focus-visible { + color: var(--sun); +} + +@keyframes socialOrbit { + 0% { + opacity: 0; + transform: scale(0.45) rotate(0deg); + } + + 45% { + opacity: 1; + } + + 100% { + opacity: 0; + transform: scale(1.35) rotate(180deg); + } +} + +@media (prefers-reduced-motion: reduce) { + #footer > div:first-child > a, + img.socialIcon { + transition: none; + } + + #footer > div:first-child > a:hover, + #footer > div:first-child > a:focus-visible, + #footer > div:first-child > a:hover .socialIcon, + #footer > div:first-child > a:focus-visible .socialIcon { + transform: none; + } + + #footer > div:first-child > a:hover::before, + #footer > div:first-child > a:hover::after, + #footer > div:first-child > a:focus-visible::before, + #footer > div:first-child > a:focus-visible::after { + animation: none; + } +} + +:root { + --ink: #173f5f; + --paper: #faf8f3; + --sun: #f6c85f; + --coral: #ed553b; + --muted: #52616b; + --line: rgba(23, 63, 95, 0.2); +} + +html { + scroll-behavior: smooth; +} + +body { + background: var(--paper); + color: var(--ink); + font-family: "Montserrat", sans-serif; +} + +section { + box-sizing: border-box; + width: 100%; +} + +section.padding { + padding: 8rem max(2rem, calc((100vw - 1120px) / 2)); +} + +h1, +h2, +h3, +p, +ul { + max-width: 100%; +} + +h1 { + color: var(--paper); + font-size: clamp(4rem, 10vw, 8.5rem); + letter-spacing: 0; + line-height: 0.86; + max-width: 9ch; + text-wrap: balance; +} + +h2 { + color: var(--ink); + font-size: clamp(2rem, 4vw, 4rem); + letter-spacing: 0; + line-height: 1; + padding-top: 0; + text-wrap: balance; +} + +h3 { + color: var(--ink); + font-size: clamp(1.5rem, 3vw, 2.5rem); + line-height: 1.05; +} + +p { + color: var(--muted); + font-size: 1rem; + line-height: 1.65; +} + +p.large { + color: var(--ink); + font-size: clamp(1.2rem, 2vw, 1.65rem); + line-height: 1.45; +} + +a { + color: inherit; + transition: color 180ms ease, transform 180ms ease; +} + +a:hover, +a:focus-visible { + color: var(--coral); +} + +#home { + align-items: center; + background: var(--ink); + display: flex; + min-height: 100vh; + overflow: hidden; +} + +.heroLayout { + display: block; + margin: auto 0; + position: relative; + width: 100%; + z-index: 1; +} + +.heroContent { + max-width: 720px; + padding: 8rem 0 6rem; + position: relative; + z-index: 1; +} + +.heroContent .eyebrow, +.heroContent h1, +.heroContent h2, +.heroContent .heroIntro { + animation: heroRise 800ms cubic-bezier(0.2, 0.8, 0.2, 1) both; +} + +.heroContent h1 { + animation-delay: 120ms; +} + +.heroContent h2 { + animation-delay: 220ms; +} + +.heroContent .heroIntro { + animation-delay: 320ms; +} + +.heroImageLayer { + animation: heroImageReveal 1400ms ease-out both; + inset: 0; + overflow: hidden; + pointer-events: none; + position: absolute; + z-index: -1; +} + +.heroImageLayer::after { + background: linear-gradient(90deg, var(--ink) 0%, rgba(23, 63, 95, 0.88) 42%, rgba(23, 63, 95, 0.42) 78%, rgba(23, 63, 95, 0.2) 100%); + content: ""; + inset: 0; + position: absolute; +} + +.heroImageLayer img { + height: 100%; + object-fit: cover; + object-position: center 42%; + opacity: 0.62; + transform: scale(1.04); + width: 100%; +} + +.eyebrow, +.projectNumber, +.projectMeta, +figcaption { + color: var(--coral); + font-size: 0.72rem; + font-weight: 800; + letter-spacing: 0.12em; + line-height: 1.4; + margin: 0 0 1rem; + text-transform: uppercase; +} + +.heroContent h2 { + color: var(--sun); + font-size: clamp(1.4rem, 3vw, 2.25rem); + margin-top: 1.5rem; +} + +.heroIntro { + color: rgba(250, 248, 243, 0.86); + font-size: 1.1rem; + margin-top: 2rem; + max-width: 34rem; +} + +.scrollHint { + bottom: 2rem; + color: var(--paper); + font-size: 0.75rem; + font-weight: 800; + letter-spacing: 0.12em; + position: absolute; + right: max(2rem, calc((100vw - 1120px) / 2)); + text-transform: uppercase; + z-index: 1; +} + +.scrollHint::after { + background: var(--sun); + content: ""; + display: block; + height: 2px; + margin-top: 0.5rem; + transform-origin: left; + animation: linePulse 1.8s ease-in-out infinite; + width: 100%; +} + +#about { + display: grid; + gap: 5rem; + grid-template-columns: minmax(0, 1.1fr) minmax(280px, 0.9fr); +} + +.sectionIntro { + max-width: 620px; +} + +.sectionIntro > p:last-child { + border-left: 3px solid var(--sun); + margin-top: 2rem; + padding-left: 1.25rem; +} + +.skillsPanel { + align-self: start; + background: var(--sun); + padding: 2rem; +} + +.skillsPanel .eyebrow { + color: var(--ink); +} + +.skillsList { + display: grid; + gap: 0.6rem 1.5rem; + grid-template-columns: repeat(2, minmax(0, 1fr)); + list-style: none; + margin: 0; + padding: 0; +} + +.skillsList li, +.resumeDetails li { + color: var(--ink); + font-size: 0.95rem; + line-height: 1.4; +} + +.skillsList li::before { + color: var(--coral); + content: "+"; + font-weight: 800; + margin-right: 0.45rem; +} + +.resumeDetails { + border-top: 1px solid var(--line); + display: grid; + gap: 2rem; + grid-column: 1 / -1; + grid-template-columns: repeat(3, minmax(0, 1fr)); + padding-top: 2.5rem; +} + +.resumeDetails ul { + margin: 0; + padding-left: 1rem; +} + +.resumeDetails li + li { + margin-top: 0.7rem; +} + +#portfolio { + background: #e8f1f2; +} + +.sectionHeading { + align-items: end; + display: flex; + gap: 3rem; + justify-content: space-between; + margin-bottom: 4rem; +} + +.sectionHeading > p { + max-width: 18rem; +} + +.projectFeature { + background: var(--paper); + display: grid; + grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr); + min-height: 430px; + transition: transform 350ms cubic-bezier(0.2, 0.8, 0.2, 1), box-shadow 350ms ease; +} + +.projectFeature:hover { + box-shadow: 0 22px 50px rgba(23, 63, 95, 0.14); + transform: translateY(-8px); +} + +.projectCopy { + display: flex; + flex-direction: column; + justify-content: center; + padding: clamp(2rem, 5vw, 5rem); +} + +.projectCopy h3 { + margin: 0.5rem 0 1.25rem; +} + +.projectMeta { + color: var(--muted); + margin-top: 2rem; +} + +.projectFeature > img { + height: 100%; + min-height: 430px; + object-fit: cover; + width: 100%; +} + +.imageGallery { + display: grid; + gap: 2rem; + grid-template-columns: repeat(2, minmax(0, 1fr)); + margin-top: 2rem; +} + +figure { + margin: 0; +} + +.imageGallery img { + aspect-ratio: 1.35; + display: block; + object-fit: cover; + width: 100%; + transition: filter 350ms ease, transform 350ms cubic-bezier(0.2, 0.8, 0.2, 1); +} + +.imageGallery figure { + overflow: hidden; +} + +.imageGallery figure:hover img, +.imageGallery figure:focus-within img { + filter: saturate(1.15) contrast(1.05); + transform: scale(1.04); +} + +figcaption { + color: var(--muted); + margin-top: 0.75rem; +} + +#footer { + align-items: flex-start; + box-sizing: border-box; + display: flex; + gap: 2rem; + padding: 6rem max(2rem, calc((100vw - 1120px) / 2)) 3rem !important; +} + +.contactIntro h2 { + color: var(--paper); +} + +.contactIntro p:last-child { + color: rgba(250, 248, 243, 0.7); +} + +#footer > div:first-child { + display: flex; + flex-wrap: wrap; + gap: 1rem; + margin-left: auto; + max-width: 24rem; +} + +#footer > div:first-child > a { + border: 1px solid rgba(250, 248, 243, 0.45); + display: inline-flex; + height: auto; + padding: 0.8rem 1rem; + width: auto; +} + +#footer > p { + border-top: 1px solid rgba(250, 248, 243, 0.2); + color: rgba(250, 248, 243, 0.7) !important; + margin-top: 2rem; + padding-top: 1rem; + width: 100%; +} + +@media (max-width: 720px) { + section.padding { + padding: 6rem 1.25rem; + } + + #home { + min-height: 720px; + } + + .heroContent { + padding: 7rem 0 5rem; + } + + .heroImageLayer img { + object-position: 58% center; + } + + #about, + .projectFeature, + .resumeDetails { + grid-template-columns: 1fr; + } + + #about { + gap: 3rem; + } + + .sectionHeading { + align-items: flex-start; + flex-direction: column; + gap: 1rem; + margin-bottom: 2.5rem; + } + + .projectFeature > img { + min-height: 280px; + order: -1; + } + + .imageGallery { + grid-template-columns: 1fr; + } + + #footer { + padding-left: 1.25rem !important; + padding-right: 1.25rem !important; + } + + #footer > div:first-child { + margin-left: 0; + } + + .workAreas { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .workArea { + min-height: 4rem; + padding: 0.75rem; + } + + .workArea span { + font-size: 0.65rem; + } +} + +@keyframes heroRise { + from { + opacity: 0; + transform: translateY(1.5rem); + } + + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes heroImageReveal { + from { + opacity: 0; + transform: scale(1.1); + } + + to { + opacity: 1; + transform: scale(1.04); + } +} + +@keyframes linePulse { + 0%, + 100% { + opacity: 0.45; + transform: scaleX(0.55); + } + + 50% { + opacity: 1; + transform: scaleX(1); + } +} + +@media (prefers-reduced-motion: reduce) { + html { + scroll-behavior: auto; + } + + .heroContent .eyebrow, + .heroContent h1, + .heroContent h2, + .heroContent .heroIntro, + .scrollHint::after { + animation: none; + } + + .heroImageLayer { + animation: none; + } + + .projectFeature, + .imageGallery img { + transition: none; + } + + .projectFeature:hover { + transform: none; + } }