[
  {
    "path": ".eslintrc.json",
    "content": "{\n  \"extends\": \"next/core-web-vitals\",\n  \"rules\": {\n    \"react/no-unescaped-entities\": 0,\n    \"@next/next/no-img-element\": \"off\"\n  }\n}"
  },
  {
    "path": ".gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pnp\n.pnp.js\n\n# testing\n/coverage\n\n# next.js\n/.next/\n/out/\n\n# production\n/build\n\n# private markdown\nmeta.md\n\n# misc\n.DS_Store\n*.pem\n\n# debug\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# local env files\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\n\n# vercel\n.vercel\n\n\n# rss\n/public/rss\n/public/feed.xml\npublic/sitemap.xml\n\n\n# pwa\n/public/sw.js\n/public/workbox-*.js\n/public/worker-*.js\n/public/sw.js.map\n/public/workbox-*.js.map\n/public/worker-*.js.map\n\n\n/store\n\ntsconfig.tsbuildinfo\n.env*.local\n\n.vscode/"
  },
  {
    "path": ".vercelignore",
    "content": "/sanity"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021 Jatin Sharma\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<div align=\"center\">\n\n![Cover](https://imgur.com/Kpzk2LQ.png)\n\n![Github stars](https://img.shields.io/github/stars/j471n/j471n.in?style=flat-square)\n![Github Forks](https://img.shields.io/github/forks/j471n/j471n.in?style=flat-square)\n![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/j471n/j471n.in?style=flat-square)\n![GitHub repo size](https://img.shields.io/github/repo-size/j471n/j471n.in?style=flat-square)\n\n</div>\n\n## Tools Used\n\n- **Framework**: [Next.js](https://nextjs.org/)\n- **Styling**: [Tailwind CSS](https://tailwindcss.com/)\n- **Content**: [MDX](https://github.com/mdx-js/mdx)\n\n- **Database**: [Supabase](https://supabase.com/)\n- **Animations**: [Framer Motion](https://framer.com/motion)\n- **Deployment**: [Vercel](https://vercel.com)\n- **Icons**: [React Icons](https://react-icons.github.io/react-icons/)\n- **Plugins**: [rehype](https://github.com/rehypejs/rehype)\n- **Analytics**: [Google Analytics](https://analytics.google.com/analytics/web/)\n- [SWR](https://swr.vercel.app/)\n- [Email.js](https://www.emailjs.com/)\n- [React Toastify](https://github.com/fkhadra/react-toastify)\n\n## Run Locally\n\nClone the project:\n\n```bash\ngit clone https://github.com/j471n/j471n.in.git\n```\n\nGo to the project directory:\n\n```bash\ncd j471n.in\n```\n\nInstall dependencies\n\n```bash\nyarn\n# or\nnpm install\n```\n\nStart the server:\n\n```bash\nyarn dev\n# or\nnpm run dev\n```\n\nAfter that server should be running on [localhost:3000](http://localhost:3000)\n\n> I am using [yarn](https://yarnpkg.com/) you can use [pnpm](https://pnpm.io/) or [npm](https://www.npmjs.com/)\n\n> Warning: You could run into errors if you don't populate the `.env.local` with the correct values\n\n## Setting up the Environment\n\nRename [`.env.example`](/.env.example) to `.env.local` and then you need to populate that with the respective values.\n\n### Email Service Integration\n\n- `NEXT_PUBLIC_YOUR_SERVICE_ID`: Go to the [Admin Panel](https://dashboard.emailjs.com/admin) of [emailjs.com](https://emailjs.com). If you haven't already added a service then Click on the **Add Service** Button as shown in the image\n\n  ![](https://i.imgur.com/bK5wzkD.png)\n\n  Then choose any method you want I am using **Gmail**\n\n  ![](https://i.imgur.com/zTrFCNJ.png)\n\n  - Then first click on the **Connect Account and log** in with your Gmail account that you want to use to get the emails from.\n  - In the second step click on **Create Service** and then copy the **Service ID** and add this ID to `NEXT_PUBLIC_YOUR_SERVICE_ID` in `.env.local`\n\n    ![](https://i.imgur.com/c8ZkUf5.png)\n\n- `NEXT_PUBLIC_YOUR_TEMPLATE_ID`: To get the Template ID visit the [Email Templates](https://dashboard.emailjs.com/admin/templates) section and click on **Create New Template**.\n\n  ![](https://i.imgur.com/TQLrQuz.png)\n\n  And then you will see a window where you can edit your email template after you are satisfied with your template then click on the Save button in the top right corner.\n\n  ![](https://i.imgur.com/98adqhN.png)\n\n  After that you will have your Template ID as shown in the image below:\n\n  ![](https://i.imgur.com/pcqKu3f.png)\n\n- `NEXT_PUBLIC_YOUR_USER_ID`: To get your User ID, Go to [Account](https://dashboard.emailjs.com/admin/account) and then you will be able to see it:\n\n  ![](https://i.imgur.com/oU3tBiY.png)\n\n### dev\\.to Integration\n\n- `NEXT_PUBLIC_BLOGS_API`: I am using [Dev.to API](https://developers.forem.com/api) to fetch all the blog stats. You can get this API at the bottom of the [Extensions](https://dev.to/settings/extensions) section.\n  ![](https://i.imgur.com/zh7V0ZB.png)\n\n### Google Analytics\n\n- `NEXT_PUBLIC_GA_MEASUREMENT_ID`: You can follow this [guide](https://support.google.com/analytics/answer/9539598?hl=en) to get your Google Analytics ID and then you will be able to use Google Analytics in this project.\n\n- [**Google Analytics Data API**](https://developers.google.com/analytics/devguides/reporting/data/v1): I am using this API to get the analytics of this website so that I can show how many user visit this site in the last 7 days. In this you will need the value of the following properties:\n  - `GA_PROPERTY_ID`\n  - `GA_CLIENT_EMAIL`\n  - `GA_PRIVATE_KEY`\n    I have written a [blog](https://j471n.in/blogs/google-analytics-data-api) that shows how you can get these properties and guides to use them.\n\n### Spotify Integration\n\nI have used [Spotify API](https://developer.spotify.com/documentation/web-api/). So, you need three Environment Variable values-\n\n- `SPOTIFY_CLIENT_ID`\n- `SPOTIFY_CLIENT_SECRET`\n- `SPOTIFY_REFRESH_TOKEN`\n\nYou need to follow this [blog](https://j471n.in/blogs/spotify-api-nextjs) to get these variables' values.\n\n### Supabase Integration\n\nI am using [Supabase](https://supabase.com/) with ISR to store all my projects and certificates for now. It provides an API that helps you to access the data. To access that data you need two things:\n\n- `SUPABASE_URL`: Database URL.\n- `SUPABASE_KEY`: It is safe to be used in a browser context.\n\n**Steps-**\n\n- To get these go to [Supabase](https://app.supabase.com/sign-in) and log in with your account.\n\n- Click on **New Project** and fill all the fields.\n\n- Click on **Create New Project**.\n\n- Go to the [Settings](https://app.supabase.com/project/_/settings/general) page in the Dashboard.\n\n- Click **API** in the sidebar.\n\n- Find your API **URL** and **anon** key on this page.\n\n- Now you can [Create table](https://app.supabase.com/project/_/editor) and start using it.\n\n  But before you use this there was one issue I had when I was using this it was returning the empty array ([]). It was because of project policies. By default, no-one has access to the data. To fix that you can do the following:\n\n- Go to [Policies](https://app.supabase.com/project/_/auth/policies).\n\n- Select your Project.\n\n- Click on **New Policy**.\n\n  ![](https://i.imgur.com/RsGd8oW.png)\n\n- You will be presented with two options. You can choose either one. I chose the 1st option:\n\n  ![](https://i.imgur.com/QDAePUQ.png)\n\n- After that, you will have four options as shown in the following image. You can choose according to your need. I only need the read access so I went with 1st option.\n\n  ![](https://i.imgur.com/h1hSivF.png)\n\n- Click on **Use this template**.\n\n- Click on **Review**.\n\n- Click on **Save Policy**\n\n  After that, you will be able to access the data using [@supabase/supabase-js](https://www.npmjs.com/package/@supabase/supabase-js). Install it and you just set up your project with Supabase.\n\n- `REVALIDATE_SECRET`: As I am using [Supabase](https://supabase.com/), It has a feature called [webhooks](https://supabase.com/docs/guides/database/webhooks) which allow you to send real-time data from your database to another system whenever a table event occurs. So I am using it to revalidate my `projects` and `certificates` page. For that I am providing a custom secret value to verify that request is coming from authenticated source. Let's create webhook:\n\n  - Go to [webhooks](https://app.supabase.com/project/_/database/hooks) page.\n  - Click on **Create a new hook**\n  - Enter the name of the function hook (example: `update_projects`)\n\n    ![](https://i.imgur.com/QAYIkKZ.png)\n\n  - Choose your table from the dropdown list\n\n    ![](https://i.imgur.com/Hspecbe.png)\n\n  - Select events which will trigger this function hook\n\n    ![](https://i.imgur.com/OYq1qcg.png)\n\n  - Now Choose POST method and enter the revalidate URL (request will be sent to this URL)\n\n    ![](https://i.imgur.com/lpicIsR.png)\n\n  - Then add two HTTP Params `secret` and `revalidateUrl`\n\n  ![](https://i.imgur.com/Mw1Ia0o.png)\n\n  - Now add this secret to your `env.local` and it will update the page when you made some changes to your supabase database.\n  - `pages/api/revalidate.ts` is using `revalidateUrl` to update the page with new data.\n\n### GitHub Integration\n\nTo get `GITHUB_TOKEN` Follow these steps to generate a GitHub token that I am using fetch my GitHub details:\n\n**Step 1: Accessing GitHub Developer Settings**\n\n- Log in to your GitHub account.\n- Click on your profile picture in the top-right corner of the page.\n- From the drop-down menu, select Settings.\n\n![](https://i.imgur.com/h7jtNeH.png)\n\n**Step 2: Navigating to Developer Settings**\n\nIn the left sidebar, scroll down and click on Developer settings.\n\n![](https://i.imgur.com/JHFdEhP.png)\n\n**Step 3: Creating a New Personal Access Token**\n\n- In the Developer settings page, click on Personal access tokens and then Click on Tokens (Classic).\n\n  ![](https://i.imgur.com/f2eY9vB.png)\n\n- Next, click on the Generate new token button.\n\n  ![](https://i.imgur.com/V7gBKQh.png)\n\n- After selecting the necessary permissions, click on the Generate token button at the bottom of the page.\n- GitHub will generate a new token for you. Make sure to copy the token value.\n- **Important**: Treat this token like a password and keep it secure. Do not share it publicly or commit it to a version control repository.\n\n### Email Validation Integration\n\nTo get `EMAIL_VALIDATION_API` follow the following steps to get the `API_KEY` to validate the email address for the newsletter:\n\n- You need to have an account on [RapidAPI](https://rapidapi.com/).\n- If you have an account then you can just [subscribe](https://rapidapi.com/Top-Rated/api/e-mail-check-invalid-or-disposable-domain/pricing) the free version of [E-mail Check Invalid or Disposable Domain](https://rapidapi.com/Top-Rated/api/e-mail-check-invalid-or-disposable-domain/). Which will give you the 1000 request/month.\n\n  ![Rapid API-1](https://imgur.com/OMFF69O.png)\n\n- Then you'll get the `API_KEY`, which you can store in your `.env.local`.\n\n  ![Rapid API-2](https://imgur.com/REdKVsX.png)\n\n### Sanity Integration\n\n- `SANITY_PROJECT_ID`:\n\n  - Go to the [Sanity.io](<(https://www.sanity.io/)>) website using your web browser.\n  - Login with you account/Create a new account.\n  - After logging in, you'll be redirected to the Sanity.io dashboard.\n  - If you have an existing project, you'll see it listed on the dashboard. Click on the project's name to access it.\n  - Once you're inside the project, look at the browser's address bar. The URL should look something like this: `https://www.sanity.io/manage/project/your-project-id`\n  - The your-project-id in the URL is your Sanity project ID. It's a unique identifier for your specific project.\n\n  That's it! You've now obtained your Sanity project ID, which you can use for interacting with your Sanity project via its API or other integrations.\n\n### TMDB Integration\n\n- `TMDB_ACCOUNT_ID` and `TMDB_ACCESS_TOKEN`: To enable seamless integration of movie and TV show data, we will use the TMDB API, which offers comprehensive information about media content. The following steps will guide you:\n\n  **1. Overview of TMDB Integration**\n\n  Previously, movie and TV show data were manually stored using Supabase, requiring tedious manual work. To streamline the process and automatically update ratings, we have switched to TMDB (The Movie Database).\n\n  **2. Creating or Logging into Your TMDB Account**\n\n  If you already have a TMDB account, log in with your existing credentials. Otherwise, visit TMDB's website and create a new account.\n\n  **3. Generating API Key**\n\n  After logging in, navigate to the API section in your account settings. Here, you can generate a new API key to access TMDB's data and services.\n\n  ![generate api key](https://i.imgur.com/y0wA21L.png)\n\n  **Completing the API Key Request Form**\n\n  Fill in all the required details in the API key request form, and make sure to accept the terms and conditions.\n\n  ![complete api key request form](https://i.imgur.com/FZ1RdPf.png)\n\n  **Obtaining API Key and Access Token**\n\n  Once you have completed the application registration, you will receive an API key and an access token. Assign the access token to the `TMDB_ACCESS_TOKEN` variable.\n\n  ![API Key and Access Token](https://i.imgur.com/Q6LI6EF.png)\n\n  **Finding Your TMDB Account ID**\n\n  To get the `TMDB_ACCOUNT_ID`, log in to the TMDB system and visit the developer website. There, you will find your account ID associated with your account.\n\n  ![Finding Your TMDB Account ID](https://i.imgur.com/AdEPtb9.png)\n\n  With the `TMDB_ACCOUNT_ID` and `TMDB_ACCESS_TOKEN` acquired from the steps above, you can now seamlessly access and update movie and TV show data through TMDB's API, automating the process and making it significantly more efficient. Enjoy your improved movie and TV show list management experience!\n\n## Supabase Database Schema\n\n### Table: certificates\n\n| Column Name   | Data Type                   | Constraints                                       |\n| ------------- | --------------------------- | ------------------------------------------------- |\n| `id`          | UUID                        | NOT NULL, PRIMARY KEY, DEFAULT uuid_generate_v4() |\n| `title`       | TEXT                        | NOT NULL                                          |\n| `issued_date` | DATE                        | -                                                 |\n| `org_name`    | TEXT                        | -                                                 |\n| `org_logo`    | TEXT                        | -                                                 |\n| `url`         | TEXT                        | -                                                 |\n| `pinned`      | BOOLEAN                     | -                                                 |\n| `created_at`  | TIMESTAMP WITHOUT TIME ZONE | NOT NULL, DEFAULT now()                           |\n\n### Table: movies\n\n| Column Name  | Data Type                | Constraints                                             |\n| ------------ | ------------------------ | ------------------------------------------------------- |\n| `id`         | BIGINT                   | GENERATED BY DEFAULT AS IDENTITY, NOT NULL, PRIMARY KEY |\n| `created_at` | TIMESTAMP WITH TIME ZONE | NOT NULL, DEFAULT (now() AT TIME ZONE 'utc'::text)      |\n| `name`       | TEXT                     | -                                                       |\n| `image`      | TEXT                     | -                                                       |\n| `url`        | TEXT                     | -                                                       |\n| `year`       | INTEGER                  | -                                                       |\n| `watched`    | BOOLEAN                  | DEFAULT true                                            |\n| `rating`     | SMALLINT                 | -                                                       |\n\n### Table: projects\n\n| Column Name   | Data Type                   | Constraints                                       |\n| ------------- | --------------------------- | ------------------------------------------------- |\n| `id`          | UUID                        | NOT NULL, PRIMARY KEY, DEFAULT uuid_generate_v4() |\n| `created_at`  | TIMESTAMP WITHOUT TIME ZONE | -                                                 |\n| `name`        | TEXT                        | -                                                 |\n| `description` | TEXT                        | -                                                 |\n| `githubURL`   | TEXT                        | -                                                 |\n| `previewURL`  | TEXT                        | -                                                 |\n| `tools`       | TEXT[]                      | -                                                 |\n| `pinned`      | BOOLEAN                     | DEFAULT false                                     |\n| `coverImage`  | TEXT                        | -                                                 |\n\n### Table: views\n\n| Column Name | Data Type | Constraints           |\n| ----------- | --------- | --------------------- |\n| `slug`      | TEXT      | NOT NULL, PRIMARY KEY |\n| `views`     | BIGINT    | -                     |\n\n### Table: user_data\n\n| Column Name  | Data Type                | Constraints                                      |\n| ------------ | ------------------------ | ------------------------------------------------ |\n| `id`         | UUID                     | NOT NULL, PRIMARY KEY, DEFAULT gen_random_uuid() |\n| `key`        | TEXT                     | -                                                |\n| `value`      | TEXT                     | -                                                |\n| `created_at` | TIMESTAMP WITH TIME ZONE | DEFAULT now()                                    |\n\nThis table is unique because it accommodates various types of miscellaneous data. I'm sharing the current keys in my database, which are utilized in my project. This will guide you on the types of data you need to add to avoid errors during implementation.\"\n\n| Keys                   | Sample Data                              |\n| ---------------------- | ---------------------------------------- |\n| `linkedin`             | [Sample JSON](https://traff.co/K6wi1Q3p) |\n| `instagram_user_token` | [Sample TEXT](https://traff.co/mgFnU8vN) |\n| `devto_stats`          | [Sample JSON](https://traff.co/Ff99Gv2h) |\n\n## Documentation\n\nI have written an in-depth blog on [How I Made My Portfolio with Next.js](https://dev.to/j471n/how-i-made-my-portfolio-with-nextjs-2mn3). You can visit there to look at the detailed guide about this portfolio.\n"
  },
  {
    "path": "components/Blog.tsx",
    "content": "import { BlogPost } from \"@lib/interface/sanity\";\nimport Image from \"next/image\";\nimport Link from \"next/link\";\nimport { getFormattedDate } from \"@utils/date\";\nimport { motion } from \"framer-motion\";\nimport { FiArrowRight } from \"react-icons/fi\";\n\nconst cardVariants = {\n  hidden: { opacity: 0, y: 14 },\n  visible: {\n    opacity: 1,\n    y: 0,\n    transition: { type: \"spring\" as const, stiffness: 120, damping: 20 },\n  },\n};\n\nexport default function Blog({\n  blog,\n  animate = false,\n  index,\n}: {\n  blog: BlogPost;\n  animate?: boolean;\n  index?: number;\n}) {\n  return (\n    <motion.article\n      variants={cardVariants}\n      initial={animate ? \"hidden\" : false}\n      whileInView={animate ? \"visible\" : undefined}\n      viewport={{ once: true }}\n      className=\"group relative border-b border-gray-300 dark:border-neutral-700 last:border-0\"\n    >\n      {/* Left accent bar */}\n      <div className=\"absolute left-0 inset-y-0 w-0.5 bg-gray-900 dark:bg-white origin-center scale-y-0 group-hover:scale-y-100 transition-transform duration-200 rounded-sm\" />\n\n      <Link\n        href={`/blogs/${blog.slug.current}`}\n        className=\"flex items-center gap-4 sm:gap-6 py-6 pl-4 pr-2 hover:bg-gray-50/70 dark:hover:bg-gray-900/20 transition-colors duration-150 rounded-r-xl\"\n      >\n        {/* Article index */}\n        {index !== undefined && (\n          <span className=\"text-[10px] font-mono text-gray-300 dark:text-gray-700 w-5 flex-shrink-0 select-none self-start mt-1.5\">\n            {String(index + 1).padStart(2, \"0\")}\n          </span>\n        )}\n\n        {/* Content */}\n        <div className=\"flex-1 min-w-0 space-y-2\">\n          <h3 className=\"font-bold text-lg leading-snug text-gray-900 dark:text-white line-clamp-2 group-hover:text-gray-700 dark:group-hover:text-gray-300 transition-colors\">\n            {blog.title}\n          </h3>\n          <p className=\"text-sm text-gray-500 dark:text-gray-500 line-clamp-2 leading-relaxed hidden sm:block\">\n            {blog.excerpt}\n          </p>\n          {/* Meta row */}\n          <div className=\"flex items-center gap-2 pt-0.5 flex-wrap\">\n            <div className=\"relative w-4 h-4 rounded-full overflow-hidden flex-shrink-0\">\n              <Image\n                alt={blog.author.name}\n                fill\n                src={\n                  blog.organization\n                    ? blog.organization.image.asset.url\n                    : blog.author.image.asset.url\n                }\n                className=\"object-cover\"\n              />\n            </div>\n            <span className=\"text-[11px] font-mono text-gray-400 dark:text-gray-600\">\n              {blog.author.name}\n            </span>\n            {blog.organization && (\n              <>\n                <span className=\"text-gray-200 dark:text-neutral-700\">·</span>\n                <span className=\"text-[11px] font-mono text-gray-400 dark:text-gray-600\">\n                  {blog.organization.name}\n                </span>\n              </>\n            )}\n            <span className=\"text-gray-200 dark:text-neutral-700\">·</span>\n            <span className=\"text-[11px] font-mono text-gray-400 dark:text-gray-600\">\n              {getFormattedDate(new Date(blog.publishedAt))}\n            </span>\n          </div>\n        </div>\n\n        {/* Thumbnail — grayscale at rest, color on hover */}\n        {/* <div className=\"relative w-20 h-16 sm:w-28 sm:h-20 rounded-xl overflow-hidden flex-shrink-0 border border-gray-100 dark:border-neutral-700 shadow-sm\">\n          <Image\n            title={blog.title}\n            alt={blog.title}\n            src={blog.mainImage.asset.url}\n            fill\n            quality={80}\n            className=\"object-cover grayscale group-hover:grayscale-0 transition-all duration-500\"\n          />\n        </div> */}\n\n        {/* Arrow */}\n        <FiArrowRight className=\"w-4 h-4 text-gray-300 dark:text-gray-700 group-hover:text-gray-900 dark:group-hover:text-white group-hover:translate-x-0.5 transition-all flex-shrink-0 hidden sm:block\" />\n      </Link>\n    </motion.article>\n  );\n}\n"
  },
  {
    "path": "components/BookCard.tsx",
    "content": "import Image from \"next/image\";\nimport Link from \"next/link\";\nimport { motion } from \"framer-motion\";\nimport { HiOutlineExternalLink } from \"react-icons/hi\";\nimport { HardcoverBook } from \"@lib/types\";\n\nconst itemVariants = {\n  hidden: { opacity: 0, y: 16 },\n  visible: {\n    opacity: 1,\n    y: 0,\n    transition: { type: \"spring\", stiffness: 260, damping: 24 },\n  },\n};\n\nconst HARDCOVER_BOOK_URL = \"https://hardcover.app/books/\";\nconst FALLBACK_COVER = \"https://imgur.com/5dYYce8.png\";\n\nconst STAR_PATH =\n  \"M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z\";\n\nfunction Stars({\n  rating,\n  size = \"w-3 h-3\",\n}: {\n  rating: number;\n  size?: string;\n}) {\n  const filled = Math.round(rating);\n  return (\n    <>\n      {[1, 2, 3, 4, 5].map((i) => (\n        <svg\n          key={i}\n          className={`${size} ${i <= filled ? \"text-amber-400\" : \"text-gray-300 dark:text-neutral-600\"}`}\n          fill=\"currentColor\"\n          viewBox=\"0 0 20 20\"\n          aria-hidden=\"true\"\n        >\n          <path d={STAR_PATH} />\n        </svg>\n      ))}\n    </>\n  );\n}\n\nfunction formatFinishedDate(iso: string): string {\n  const d = new Date(iso);\n  return d.toLocaleDateString(\"en-US\", { month: \"short\", year: \"numeric\" });\n}\n\nexport default function BookCard({ book }: { book: HardcoverBook }) {\n  const href = book.slug\n    ? `${HARDCOVER_BOOK_URL}${book.slug}`\n    : \"https://hardcover.app\";\n\n  const authorLine =\n    book.authors.length > 0 ? book.authors.join(\", \") : \"Unknown author\";\n\n  const finishedDate =\n    book.statusId === 3\n      ? book.finishedAt\n        ? formatFinishedDate(book.finishedAt)\n        : book.updatedAt\n          ? formatFinishedDate(book.updatedAt)\n          : null\n      : null;\n\n  const hasMyRating = book.userRating != null && book.userRating > 0;\n  const hasCommunityRating = book.rating != null && book.rating > 0;\n\n  return (\n    <motion.div variants={itemVariants}>\n      <Link\n        href={href}\n        target=\"_blank\"\n        rel=\"noopener noreferrer\"\n        className=\"relative flex gap-3 p-3 border border-gray-200 dark:border-neutral-700 bg-white dark:bg-darkSecondary/10 hover:bg-gray-50 dark:hover:bg-darkSecondary/30 transition-colors group\"\n        aria-label={`${book.title} by ${authorLine} — view on Hardcover`}\n      >\n        {/* External link icon */}\n        <span className=\"absolute top-2.5 right-2.5 opacity-0 group-hover:opacity-100 transition-opacity text-gray-400 dark:text-gray-500\">\n          <HiOutlineExternalLink className=\"w-4 h-4\" />\n        </span>\n\n        {/* Cover */}\n        <div className=\"relative flex-shrink-0 w-[88px] h-[132px] overflow-hidden shadow-md\">\n          <Image\n            src={book.coverUrl ?? FALLBACK_COVER}\n            alt={`Cover of ${book.title}`}\n            fill\n            sizes=\"88px\"\n            quality={85}\n            className=\"object-cover group-hover:scale-105 transition-transform duration-500\"\n          />\n        </div>\n\n        {/* Details */}\n        <div className=\"flex flex-col min-w-0 flex-1 py-0.5 pr-5\">\n          {/* Author */}\n          <p className=\"text-[10px] font-mono text-gray-400 dark:text-gray-500 uppercase tracking-[0.12em] truncate\">\n            {authorLine}\n          </p>\n\n          {/* Title */}\n          <h3 className=\"mt-1 text-sm font-semibold text-gray-900 dark:text-white leading-snug line-clamp-2 group-hover:text-gray-600 dark:group-hover:text-gray-300 transition-colors\">\n            {book.title}\n          </h3>\n\n          {/* Spacer */}\n          <div className=\"flex-1\" />\n\n          {/* Bottom meta */}\n          <div className=\"flex flex-col gap-1.5 mt-2\">\n            {/* Ratings row — personal stars + community avg on one line */}\n            {(hasMyRating || hasCommunityRating) && (\n              <div className=\"flex items-center gap-2 flex-wrap\">\n                {hasMyRating && (\n                  <span\n                    className=\"flex items-center gap-0.5\"\n                    title={`My rating: ${book.userRating!.toFixed(1)}`}\n                  >\n                    <Stars rating={book.userRating!} />\n                    <span className=\"ml-1 text-[11px] font-medium text-amber-500 dark:text-amber-400 tabular-nums\">\n                      {book.userRating!.toFixed(1)}\n                    </span>\n                  </span>\n                )}\n\n                {hasMyRating && hasCommunityRating && (\n                  <span className=\"text-gray-300 dark:text-neutral-600 select-none\">\n                    ·\n                  </span>\n                )}\n\n                {hasCommunityRating && (\n                  <span\n                    className=\"flex items-center gap-0.5 text-[11px] text-gray-400 dark:text-gray-500 tabular-nums\"\n                    title=\"Community average rating\"\n                  >\n                    <svg\n                      className=\"w-2.5 h-2.5 text-gray-400 dark:text-gray-500\"\n                      fill=\"currentColor\"\n                      viewBox=\"0 0 20 20\"\n                      aria-hidden=\"true\"\n                    >\n                      <path d={STAR_PATH} />\n                    </svg>\n                    <span>{book.rating!.toFixed(1)}</span>\n                    <span className=\"text-[10px] ml-0.5 text-gray-400 dark:text-gray-600\">\n                      avg\n                    </span>\n                  </span>\n                )}\n              </div>\n            )}\n\n            {/* Year · pages */}\n            <div className=\"flex items-center gap-1.5 text-[11px] text-gray-500 dark:text-gray-400 tabular-nums\">\n              {book.releaseYear != null && <span>{book.releaseYear}</span>}\n              {book.releaseYear != null && book.pages != null && (\n                <span className=\"text-gray-300 dark:text-neutral-600\">·</span>\n              )}\n              {book.pages != null && (\n                <span>{book.pages.toLocaleString()} pages</span>\n              )}\n            </div>\n\n            {/* Finished date */}\n            {finishedDate && (\n              <div className=\"flex items-center gap-1 text-[11px] text-gray-500 dark:text-gray-400\">\n                <svg\n                  className=\"w-3 h-3 flex-shrink-0 text-green-500 dark:text-green-400\"\n                  fill=\"none\"\n                  strokeWidth=\"2\"\n                  stroke=\"currentColor\"\n                  viewBox=\"0 0 24 24\"\n                  aria-hidden=\"true\"\n                >\n                  <path\n                    strokeLinecap=\"round\"\n                    strokeLinejoin=\"round\"\n                    d=\"M4.5 12.75l6 6 9-13.5\"\n                  />\n                </svg>\n                <span>Finished</span>\n                <span className=\"text-gray-400 dark:text-gray-500\">·</span>\n                <span className=\"font-medium text-gray-700 dark:text-gray-300\">\n                  {finishedDate}\n                </span>\n              </div>\n            )}\n          </div>\n        </div>\n      </Link>\n    </motion.div>\n  );\n}\n"
  },
  {
    "path": "components/Contact/Contact.tsx",
    "content": "import React from \"react\";\nimport { motion } from \"framer-motion\";\nimport ContactForm from \"./ContactForm\";\nimport siteConfig from \"@content/siteConfig\";\n\nconst infoRows = [\n  { label: \"Response Time\", value: \"< 24 hours\" },\n  { label: \"Timezone\", value: \"IST · UTC +5:30\" },\n  { label: \"Preferred\", value: \"Email / LinkedIn\" },\n  { label: \"Work Type\", value: \"Remote / Contract\" },\n];\n\nexport default function Contact() {\n  const { contact } = siteConfig;\n\n  return (\n    <div id=\"contact\" className=\"relative pt-20 sm:pt-24 overflow-hidden\">\n      <div className=\"max-w-7xl mx-auto px-6 sm:px-8 lg:px-12\">\n        {/* Section number watermark */}\n        <div\n          className=\"absolute -right-2 top-6 font-black select-none pointer-events-none leading-none tracking-tighter bg-gradient-to-b from-gray-200 to-gray-50 dark:from-[#232628] dark:to-darkPrimary bg-clip-text text-transparent\"\n          style={{ fontSize: \"clamp(5rem, 16vw, 13rem)\" }}\n          aria-hidden=\"true\"\n        >\n          05\n        </div>\n\n        {/* ── Header ── */}\n        <div className=\"relative z-10 flex flex-col lg:flex-row lg:items-end lg:justify-between gap-8 mb-12\">\n          <div className=\"space-y-3 max-w-xl\">\n            <motion.div\n              initial={{ opacity: 0, x: -16 }}\n              whileInView={{ opacity: 1, x: 0 }}\n              viewport={{ once: true }}\n              className=\"flex items-center gap-3\"\n            >\n              <div className=\"h-px w-5 bg-gray-400 dark:bg-gray-600 flex-shrink-0\" />\n              <span className=\"font-mono text-[10px] tracking-[0.45em] uppercase text-gray-400 dark:text-gray-600\">\n                {contact.eyebrow}\n              </span>\n            </motion.div>\n\n            <motion.h2\n              initial={{ opacity: 0, y: 20 }}\n              whileInView={{ opacity: 1, y: 0 }}\n              viewport={{ once: true }}\n              transition={{ delay: 0.1 }}\n              className=\"text-3xl sm:text-4xl lg:text-5xl font-bold text-gray-900 dark:text-white\"\n            >\n              {contact.title}\n            </motion.h2>\n\n            <motion.p\n              initial={{ opacity: 0, y: 16 }}\n              whileInView={{ opacity: 1, y: 0 }}\n              viewport={{ once: true }}\n              transition={{ delay: 0.15 }}\n              className=\"text-base text-gray-600 dark:text-gray-400 border-l-2 border-gray-300 dark:border-gray-700 pl-4 py-0.5\"\n            >\n              {contact.description}\n            </motion.p>\n          </div>\n\n          {/* Email quick-link */}\n          <motion.div\n            initial={{ opacity: 0, x: 20 }}\n            whileInView={{ opacity: 1, x: 0 }}\n            viewport={{ once: true }}\n            transition={{ delay: 0.15 }}\n            className=\"flex flex-col items-start lg:items-end gap-1 flex-shrink-0\"\n          >\n            <span className=\"text-[10px] font-mono uppercase tracking-[0.3em] text-gray-400 dark:text-gray-600\">\n              Or reach me directly\n            </span>\n            <a\n              href={`mailto:${siteConfig.person.email}`}\n              className=\"text-sm font-semibold text-gray-900 dark:text-white hover:underline underline-offset-4 decoration-gray-400 font-mono\"\n            >\n              {siteConfig.person.email}\n            </a>\n          </motion.div>\n        </div>\n\n        {/* ── Divider ── */}\n        <motion.div\n          initial={{ opacity: 0 }}\n          whileInView={{ opacity: 1 }}\n          viewport={{ once: true }}\n          className=\"flex items-center gap-4 mb-12\"\n        >\n          <div className=\"h-px flex-1 bg-gray-300 dark:bg-neutral-700\" />\n          <span className=\"font-mono text-[9px] tracking-[0.4em] uppercase text-gray-500 dark:text-gray-700\">\n            Send a Message\n          </span>\n          <div className=\"h-px flex-1 bg-gray-300 dark:bg-neutral-700\" />\n        </motion.div>\n\n        {/* ── Two-column body ── */}\n        <div className=\"grid lg:grid-cols-5 gap-12 lg:gap-16 items-start\">\n          {/* Form — 3 cols */}\n          <motion.div\n            initial={{ opacity: 0, y: 20 }}\n            whileInView={{ opacity: 1, y: 0 }}\n            viewport={{ once: true }}\n            transition={{ delay: 0.2 }}\n            className=\"lg:col-span-3\"\n          >\n            <ContactForm />\n          </motion.div>\n\n          {/* Info sidebar — 2 cols */}\n          <motion.div\n            initial={{ opacity: 0, x: 20 }}\n            whileInView={{ opacity: 1, x: 0 }}\n            viewport={{ once: true }}\n            transition={{ delay: 0.3 }}\n            className=\"lg:col-span-2 flex flex-col gap-0\"\n          >\n            {/* Status */}\n            <div className=\"pb-6\">\n              <span className=\"font-mono text-[9px] tracking-[0.4em] uppercase text-gray-400 dark:text-gray-600 mb-3 block\">\n                Current Status\n              </span>\n              <div className=\"flex items-center gap-2.5\">\n                <span className=\"relative flex h-2.5 w-2.5 flex-shrink-0\">\n                  <span className=\"animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-60\" />\n                  <span className=\"relative inline-flex rounded-full h-2.5 w-2.5 bg-emerald-500\" />\n                </span>\n                <span className=\"text-sm font-semibold text-gray-900 dark:text-white\">\n                  Available for Work\n                </span>\n              </div>\n            </div>\n\n            <div className=\"h-px bg-gray-200 dark:bg-neutral-700\" />\n\n            {/* Info rows */}\n            <div className=\"divide-y divide-gray-100 dark:divide-neutral-700\">\n              {infoRows.map(({ label, value }, i) => (\n                <motion.div\n                  key={label}\n                  initial={{ opacity: 0, y: 8 }}\n                  whileInView={{ opacity: 1, y: 0 }}\n                  viewport={{ once: true }}\n                  transition={{ delay: 0.35 + i * 0.06 }}\n                  className=\"flex justify-between items-center py-4\"\n                >\n                  <span className=\"font-mono text-[9px] tracking-[0.35em] uppercase text-gray-400 dark:text-gray-500\">\n                    {label}\n                  </span>\n                  <span className=\"text-sm font-medium text-gray-900 dark:text-white\">\n                    {value}\n                  </span>\n                </motion.div>\n              ))}\n            </div>\n\n            <div className=\"h-px bg-gray-200 dark:bg-neutral-700\" />\n\n            {/* Decorative type */}\n            <div className=\"pt-8 select-none\" aria-hidden=\"true\">\n              <p\n                className=\"font-black leading-none tracking-tighter bg-gradient-to-b from-gray-300 to-gray-100 dark:from-[#2e3133] dark:to-darkPrimary bg-clip-text text-transparent\"\n                style={{ fontSize: \"clamp(2.8rem, 6vw, 4.5rem)\" }}\n              >\n                LET&apos;S\n                <br />\n                BUILD\n                <br />\n                TOGETHER\n              </p>\n            </div>\n          </motion.div>\n        </div>\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "components/Contact/ContactForm.tsx",
    "content": "import React, { useRef, useState } from \"react\";\nimport { ToastContainer, toast } from \"react-toastify\";\nimport { useDarkMode } from \"@context/darkModeContext\";\nimport emailjs from \"@emailjs/browser\";\nimport { motion, AnimatePresence } from \"framer-motion\";\nimport { FormInput } from \"@lib/types\";\nimport siteConfig from \"@content/siteConfig\";\nimport { FiSend } from \"react-icons/fi\";\n\nconst inputVariants = {\n  hidden: { opacity: 0, x: -20 },\n  visible: {\n    opacity: 1,\n    x: 0,\n    transition: {\n      duration: 0.5,\n      ease: [0.22, 1, 0.36, 1],\n    },\n  },\n};\n\nexport default function Form() {\n  const { isDarkMode } = useDarkMode();\n  const sendButtonRef = useRef<HTMLButtonElement>(null!);\n  const formRef = useRef<HTMLFormElement>(null!);\n  const [isSubmitting, setIsSubmitting] = useState(false);\n  const [focusedField, setFocusedField] = useState<string | null>(null);\n  const { contact } = siteConfig;\n\n  const FailToastId: string = \"failed\";\n\n  function sendEmail(e: React.SyntheticEvent) {\n    e.preventDefault();\n\n    const target = e.target as typeof e.target & {\n      first_name: { value: string };\n      last_name: { value: string };\n      email: { value: string };\n      subject: { value: string };\n      message: { value: string };\n    };\n\n    const emailData = {\n      to_name: siteConfig.person.name,\n      first_name: target.first_name.value.trim(),\n      last_name: target.last_name.value.trim(),\n      email: target.email.value.trim(),\n      subject: target.subject.value.trim(),\n      message: target.message.value.trim(),\n    };\n\n    if (!validateForm(emailData) && !toast.isActive(FailToastId))\n      return toast.error(\"Please fill in all required fields\", {\n        toastId: FailToastId,\n      });\n\n    setIsSubmitting(true);\n    sendButtonRef.current.setAttribute(\"disabled\", \"true\");\n\n    const toastId = toast.loading(\"Sending your message...\");\n\n    emailjs\n      .send(\n        process.env.NEXT_PUBLIC_YOUR_SERVICE_ID!,\n        process.env.NEXT_PUBLIC_YOUR_TEMPLATE_ID!,\n        emailData!,\n        process.env.NEXT_PUBLIC_YOUR_USER_ID,\n      )\n      .then(() => {\n        formRef.current.reset();\n        toast.update(toastId, {\n          render: \"Message sent successfully!\",\n          type: \"success\",\n          isLoading: false,\n          autoClose: 4000,\n        });\n        setIsSubmitting(false);\n        sendButtonRef.current.removeAttribute(\"disabled\");\n      })\n      .catch(() => {\n        toast.update(toastId, {\n          render: \"Failed to send message. Please try again.\",\n          type: \"error\",\n          isLoading: false,\n          autoClose: 4000,\n        });\n        setIsSubmitting(false);\n        sendButtonRef.current.removeAttribute(\"disabled\");\n      });\n  }\n\n  function validateForm(data: FormInput): boolean {\n    for (const key in data) {\n      if (data[key as keyof FormInput] === \"\") return false;\n    }\n    return true;\n  }\n\n  /* Shared field class — borderless top/sides, only bottom rule */\n  const fieldCls = (name: string) =>\n    `block w-full bg-transparent border-0 border-b py-3 text-gray-900 dark:text-white placeholder-gray-400 dark:placeholder-gray-600 focus:outline-none transition-colors duration-200 ${\n      focusedField === name\n        ? \"border-gray-900 dark:border-white\"\n        : \"border-gray-300 dark:border-gray-700\"\n    }`;\n\n  return (\n    <>\n      <motion.form\n        ref={formRef}\n        initial=\"hidden\"\n        animate=\"visible\"\n        className=\"space-y-0\"\n        onSubmit={sendEmail}\n      >\n        {/* Name row */}\n        <div className=\"grid sm:grid-cols-2 gap-x-10\">\n          {/* First Name */}\n          <motion.div variants={inputVariants} className=\"py-2\">\n            <label\n              htmlFor=\"first_name\"\n              className=\"block font-mono text-[9px] tracking-[0.4em] uppercase text-gray-500 dark:text-gray-500 mb-1\"\n            >\n              First Name\n            </label>\n            <input\n              type=\"text\"\n              name=\"first_name\"\n              id=\"first_name\"\n              onFocus={() => setFocusedField(\"first_name\")}\n              onBlur={() => setFocusedField(null)}\n              className={fieldCls(\"first_name\")}\n              placeholder=\"John\"\n              required\n            />\n          </motion.div>\n\n          {/* Last Name */}\n          <motion.div variants={inputVariants} className=\"py-2\">\n            <label\n              htmlFor=\"last_name\"\n              className=\"block font-mono text-[9px] tracking-[0.4em] uppercase text-gray-500 dark:text-gray-500 mb-1\"\n            >\n              Last Name\n            </label>\n            <input\n              type=\"text\"\n              name=\"last_name\"\n              id=\"last_name\"\n              onFocus={() => setFocusedField(\"last_name\")}\n              onBlur={() => setFocusedField(null)}\n              className={fieldCls(\"last_name\")}\n              placeholder=\"Doe\"\n              required\n            />\n          </motion.div>\n        </div>\n\n        {/* Email */}\n        <motion.div variants={inputVariants} className=\"py-2\">\n          <label\n            htmlFor=\"email\"\n            className=\"block font-mono text-[9px] tracking-[0.4em] uppercase text-gray-500 dark:text-gray-500 mb-1\"\n          >\n            Email Address\n          </label>\n          <input\n            type=\"email\"\n            name=\"email\"\n            id=\"email\"\n            onFocus={() => setFocusedField(\"email\")}\n            onBlur={() => setFocusedField(null)}\n            className={fieldCls(\"email\")}\n            placeholder=\"john.doe@example.com\"\n            required\n          />\n        </motion.div>\n\n        {/* Subject */}\n        <motion.div variants={inputVariants} className=\"py-2\">\n          <label\n            htmlFor=\"subject\"\n            className=\"block font-mono text-[9px] tracking-[0.4em] uppercase text-gray-500 dark:text-gray-500 mb-1\"\n          >\n            Subject\n          </label>\n          <input\n            type=\"text\"\n            name=\"subject\"\n            id=\"subject\"\n            onFocus={() => setFocusedField(\"subject\")}\n            onBlur={() => setFocusedField(null)}\n            className={fieldCls(\"subject\")}\n            placeholder=\"Project Discussion\"\n            required\n          />\n        </motion.div>\n\n        {/* Message */}\n        <motion.div variants={inputVariants} className=\"py-2\">\n          <label\n            htmlFor=\"message\"\n            className=\"block font-mono text-[9px] tracking-[0.4em] uppercase text-gray-500 dark:text-gray-500 mb-1\"\n          >\n            Message\n          </label>\n          <textarea\n            name=\"message\"\n            id=\"message\"\n            rows={5}\n            onFocus={() => setFocusedField(\"message\")}\n            onBlur={() => setFocusedField(null)}\n            className={`${fieldCls(\"message\")} resize-none`}\n            placeholder=\"Tell me about your project or idea...\"\n            required\n          />\n        </motion.div>\n\n        {/* Submit */}\n        <motion.div variants={inputVariants} className=\"pt-8\">\n          <button\n            ref={sendButtonRef}\n            type=\"submit\"\n            disabled={isSubmitting}\n            className=\"group relative inline-flex items-center gap-3 px-7 py-3 border border-gray-900 dark:border-white text-gray-900 dark:text-white font-semibold text-sm tracking-wide overflow-hidden hover:text-white dark:hover:text-gray-900 focus:outline-none transition-colors duration-300 disabled:opacity-40 disabled:cursor-not-allowed\"\n          >\n            {/* fill layer */}\n            <span className=\"absolute inset-0 bg-gray-900 dark:bg-white translate-y-full group-hover:translate-y-0 transition-transform duration-300 ease-[cubic-bezier(0.22,1,0.36,1)]\" />\n            <span className=\"relative z-10 flex items-center gap-2\">\n              <AnimatePresence>\n                {isSubmitting ? (\n                  <motion.span\n                    key=\"submitting\"\n                    initial={{ opacity: 0, scale: 0.8 }}\n                    animate={{ opacity: 1, scale: 1 }}\n                    exit={{ opacity: 0, scale: 0.8 }}\n                    className=\"flex items-center gap-2\"\n                  >\n                    <svg\n                      className=\"animate-spin h-4 w-4\"\n                      xmlns=\"http://www.w3.org/2000/svg\"\n                      fill=\"none\"\n                      viewBox=\"0 0 24 24\"\n                    >\n                      <circle\n                        className=\"opacity-25\"\n                        cx=\"12\"\n                        cy=\"12\"\n                        r=\"10\"\n                        stroke=\"currentColor\"\n                        strokeWidth=\"4\"\n                      />\n                      <path\n                        className=\"opacity-75\"\n                        fill=\"currentColor\"\n                        d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n                      />\n                    </svg>\n                    Sending...\n                  </motion.span>\n                ) : (\n                  <motion.span\n                    key=\"send\"\n                    initial={{ opacity: 0, scale: 0.8 }}\n                    animate={{ opacity: 1, scale: 1 }}\n                    exit={{ opacity: 0, scale: 0.8 }}\n                    className=\"flex items-center gap-2\"\n                  >\n                    Send Message\n                    <FiSend className=\"w-4 h-4 group-hover:translate-x-0.5 group-hover:-translate-y-0.5 transition-transform\" />\n                  </motion.span>\n                )}\n              </AnimatePresence>\n            </span>\n          </button>\n        </motion.div>\n\n        {/* Privacy note */}\n        <motion.p\n          variants={inputVariants}\n          className=\"pt-5 font-mono text-[9px] tracking-[0.3em] uppercase text-gray-400 dark:text-gray-600\"\n        >\n          {contact.privacyNote}\n        </motion.p>\n      </motion.form>\n\n      <ToastContainer\n        theme={isDarkMode ? \"dark\" : \"light\"}\n        position=\"bottom-right\"\n        style={{ zIndex: 1000 }}\n        toastClassName=\"!rounded-xl !shadow-2xl\"\n      />\n    </>\n  );\n}\n"
  },
  {
    "path": "components/Contact/index.tsx",
    "content": "export { default } from \"./Contact\";\n"
  },
  {
    "path": "components/CreateAnIssue.tsx",
    "content": "import Link from \"next/link\";\nimport React from \"react\";\n\nexport default function CreateAnIssue() {\n  return (\n    <div className=\"grid w-full h-screen px-10 sm:px-20 place-items-center dark:text-gray-200\">\n      <p>\n        Something went wrong. I know you don't know what's the problem. So Let\n        me know by{\" \"}\n        <Link\n          href=\"https://github.com/j471n/j471n.in/issues/new\"\n          target=\"_blank\"\n          rel=\"noopener noreferrer\"\n          className=\"font-bold underline hover:text-blue-500 \"\n        >\n          creating an issue\n        </Link>{\" \"}\n        on GitHub.\n      </p>\n    </div>\n  );\n}\n"
  },
  {
    "path": "components/EpigraphCard.tsx",
    "content": "import { useState } from \"react\";\nimport { IEpigraph, EpigraphSourceType } from \"@lib/interface/sanity\";\nimport { motion } from \"framer-motion\";\nimport ReactMarkdown from \"react-markdown\";\nimport { FiCopy, FiCheck, FiSearch } from \"react-icons/fi\";\n\nconst SOURCE_LABELS: Record<EpigraphSourceType, string> = {\n  book: \"Book\",\n  movie: \"Film\",\n  tvShow: \"Series\",\n  person: \"Person\",\n  song: \"Song\",\n  podcast: \"Podcast\",\n  other: \"Other\",\n};\n\nconst rowVariants = {\n  hidden: { opacity: 0, y: 8 },\n  visible: { opacity: 1, y: 0, transition: { duration: 0.4, ease: \"easeOut\" } },\n};\n\nconst quoteComponents = {\n  p: ({ children }: { children: React.ReactNode }) => (\n    <p className=\"text-gray-900 dark:text-white text-base sm:text-lg leading-[1.85] font-normal italic mb-2 last:mb-0\">\n      {children}\n    </p>\n  ),\n  strong: ({ children }: { children: React.ReactNode }) => (\n    <strong className=\"not-italic font-semibold\">{children}</strong>\n  ),\n  em: ({ children }: { children: React.ReactNode }) => (\n    <em className=\"not-italic text-gray-500 dark:text-gray-400\">{children}</em>\n  ),\n};\n\nexport default function EpigraphCard({\n  epigraph,\n  index,\n}: {\n  epigraph: IEpigraph;\n  index: number;\n}) {\n  const [copied, setCopied] = useState(false);\n  const sourceLabel = SOURCE_LABELS[epigraph.sourceType] ?? epigraph.sourceType;\n\n  const handleCopy = () => {\n    navigator.clipboard.writeText(epigraph.quote);\n    setCopied(true);\n    setTimeout(() => setCopied(false), 2000);\n  };\n\n  return (\n    <motion.article\n      variants={rowVariants}\n      className=\"group py-6 sm:py-8\"\n      style={\n        {\n          contentVisibility: \"auto\",\n          containIntrinsicSize: \"0 120px\",\n        } as React.CSSProperties\n      }\n    >\n      {/* ── Mobile layout ── */}\n      <div className=\"flex flex-col gap-3 sm:hidden\">\n        {/* Header: index left, meta right */}\n        <div className=\"flex items-center gap-3\">\n          <span className=\"font-mono text-[10px] text-gray-300 dark:text-neutral-700 tabular-nums select-none shrink-0\">\n            {String(index + 1).padStart(3, \"0\")}\n          </span>\n          <div className=\"flex items-center gap-2 ml-auto\">\n            {epigraph.year && (\n              <span className=\"font-mono text-[9px] text-gray-400 dark:text-neutral-600 select-none\">\n                {epigraph.year}\n              </span>\n            )}\n            <span className=\"font-mono text-[8px] tracking-[0.3em] uppercase text-gray-600 dark:text-gray-500 border border-gray-200 dark:border-neutral-700 px-1.5 py-0.5 leading-none\">\n              {sourceLabel}\n            </span>\n          </div>\n        </div>\n\n        {/* Quote */}\n        <blockquote className=\"border-l-2 border-gray-200 dark:border-neutral-700 group-hover:border-gray-900 dark:group-hover:border-white transition-colors duration-200 pl-3\">\n          <ReactMarkdown components={quoteComponents}>\n            {epigraph.quote}\n          </ReactMarkdown>\n        </blockquote>\n\n        {/* Attribution */}\n        <Attribution epigraph={epigraph} />\n\n        {/* Footer: tags + copy */}\n        <Footer\n          tags={epigraph.tags}\n          copied={copied}\n          onCopy={handleCopy}\n          searchQuery={epigraph.sourceTitle}\n        />\n      </div>\n\n      {/* ── Desktop layout: three-column ledger ── */}\n      <div className=\"hidden sm:grid grid-cols-[5rem_1px_1fr] gap-0\">\n        {/* Left: index + rotated source + year */}\n        <div className=\"flex flex-col items-end gap-3 pr-6 pt-0.5\">\n          <span className=\"font-mono text-[10px] text-gray-300 dark:text-neutral-700 tabular-nums select-none leading-none\">\n            {String(index + 1).padStart(3, \"0\")}\n          </span>\n          <span\n            className=\"font-mono text-[8px] tracking-[0.4em] uppercase text-gray-400 dark:text-neutral-600 select-none flex-1 flex items-center\"\n            style={\n              {\n                writingMode: \"vertical-lr\",\n                transform: \"rotate(180deg)\",\n              } as React.CSSProperties\n            }\n          >\n            {sourceLabel}\n          </span>\n          {epigraph.year && (\n            <span className=\"font-mono text-[9px] text-gray-400 dark:text-neutral-600 select-none\">\n              {epigraph.year}\n            </span>\n          )}\n        </div>\n\n        {/* Vertical rule */}\n        <div className=\"w-px bg-gray-200 dark:bg-neutral-800 group-hover:bg-gray-900 dark:group-hover:bg-white transition-colors duration-200\" />\n\n        {/* Right: quote + attribution + footer */}\n        <div className=\"flex flex-col gap-3 pl-7\">\n          <blockquote>\n            <ReactMarkdown components={quoteComponents}>\n              {epigraph.quote}\n            </ReactMarkdown>\n          </blockquote>\n          <Attribution epigraph={epigraph} />\n          <Footer\n            tags={epigraph.tags}\n            copied={copied}\n            onCopy={handleCopy}\n            searchQuery={epigraph.sourceTitle}\n          />\n        </div>\n      </div>\n    </motion.article>\n  );\n}\n\nfunction Attribution({ epigraph }: { epigraph: IEpigraph }) {\n  return (\n    <div className=\"flex flex-wrap items-baseline gap-x-1.5 gap-y-0.5\">\n      <span className=\"text-gray-400 dark:text-neutral-600 text-sm select-none leading-none\">\n        —\n      </span>\n      {epigraph.speaker && (\n        <>\n          <span className=\"text-sm font-medium text-gray-700 dark:text-gray-300 tracking-tight\">\n            {epigraph.speaker}\n          </span>\n          {(epigraph.sourceTitle || epigraph.sourceMeta) && (\n            <span className=\"text-gray-300 dark:text-neutral-700 text-xs select-none\">\n              ,\n            </span>\n          )}\n        </>\n      )}\n      <p className=\"text-sm text-gray-600 dark:text-gray-400 italic hover:text-gray-900 dark:hover:text-white  decoration-gray-300 dark:decoration-neutral-700 hover:decoration-gray-900 dark:hover:decoration-white transition-colors duration-150\">\n        {epigraph.sourceTitle}\n      </p>\n      {epigraph.sourceMeta && (\n        <>\n          <span className=\"text-gray-300 dark:text-neutral-700 text-xs select-none\">\n            /\n          </span>\n          <span className=\"text-xs text-gray-500 dark:text-gray-500 tracking-wide\">\n            {epigraph.sourceMeta}\n          </span>\n        </>\n      )}\n    </div>\n  );\n}\n\nfunction Footer({\n  tags,\n  copied,\n  onCopy,\n  searchQuery,\n}: {\n  tags?: string[];\n  copied: boolean;\n  onCopy: () => void;\n  searchQuery: string;\n}) {\n  return (\n    <div className=\"flex items-center gap-4 pt-0.5\">\n      {/* Copy button */}\n      <button\n        onClick={onCopy}\n        aria-label={copied ? \"Copied\" : \"Copy quote\"}\n        className={`flex items-center gap-1.5 shrink-0 transition-colors duration-150 ${\n          copied\n            ? \"text-gray-900 dark:text-white\"\n            : \"text-gray-400 dark:text-neutral-600 hover:text-gray-900 dark:hover:text-white\"\n        }`}\n      >\n        {copied ? <FiCheck size={11} /> : <FiCopy size={11} />}\n        <span className=\"font-mono text-[9px] tracking-wider uppercase\">\n          {copied ? \"Copied\" : \"Copy\"}\n        </span>\n      </button>\n\n      {/* Google search */}\n      <a\n        href={`https://www.google.com/search?q=${encodeURIComponent(searchQuery)}`}\n        target=\"_blank\"\n        rel=\"noopener noreferrer\"\n        aria-label=\"Search on Google\"\n        className=\"flex items-center gap-1.5 shrink-0 text-gray-400 dark:text-neutral-600 hover:text-gray-900 dark:hover:text-white transition-colors duration-150\"\n      >\n        <FiSearch size={11} />\n        <span className=\"font-mono text-[9px] tracking-wider uppercase\">\n          Search\n        </span>\n      </a>\n\n      {/* Tags */}\n      <div className=\"flex flex-wrap gap-3\">\n        {tags?.map((tag) => (\n          <span\n            key={tag}\n            className=\"font-mono text-[9px] tracking-widest uppercase text-gray-400 dark:text-neutral-600\"\n          >\n            #{tag}\n          </span>\n        ))}\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "components/Footer.tsx",
    "content": "import Link from \"next/link\";\nimport Image from \"next/image\";\nimport socialMedia from \"@content/socialMedia\";\nimport siteConfig from \"@content/siteConfig\";\nimport { navigationRoutes } from \"../utils/utils\";\nimport { motion } from \"framer-motion\";\nimport { SiSpotify } from \"react-icons/si\";\nimport useSWR from \"swr\";\nimport fetcher from \"../lib/fetcher\";\nimport { HiOutlineQrcode } from \"react-icons/hi\";\nimport { Song } from \"@lib/types\";\n\nexport default function Footer({\n  setShowQR,\n  showQR,\n}: {\n  setShowQR: (value: boolean) => void;\n  showQR: boolean;\n}) {\n  const { data: currentSong } = useSWR(\"/api/now-playing\", fetcher);\n  const { data: visitors } = useSWR(\"/api/ga\", fetcher);\n\n  const navLinks = navigationRoutes.slice(0, 5);\n  const moreLinks = navigationRoutes.slice(5);\n\n  return (\n    <footer className=\"w-screen font-inter print:hidden border-t border-gray-200 dark:border-neutral-700 mt-20 relative overflow-hidden\">\n      <div className=\"max-w-7xl mx-auto px-6 sm:px-8 lg:px-12 py-16\">\n        {/* ── Top: Brand + Now Playing ── */}\n        <div className=\"grid lg:grid-cols-2 gap-10 mb-10\">\n          {/* Brand */}\n          <motion.div\n            initial={{ opacity: 0, y: 16 }}\n            whileInView={{ opacity: 1, y: 0 }}\n            viewport={{ once: true }}\n          >\n            <h3 className=\"text-2xl font-bold text-gray-900 dark:text-white mb-2\">\n              {siteConfig.person.name}\n            </h3>\n            <p className=\"text-sm text-gray-600 dark:text-gray-400 mb-6 leading-relaxed max-w-xs\">\n              {siteConfig.footer.description}\n            </p>\n            {/* Visitor stat */}\n            <div className=\"flex items-center gap-3\">\n              <span className=\"font-mono text-[9px] tracking-[0.4em] uppercase text-gray-500 dark:text-gray-500\">\n                Visitors\n              </span>\n              <div className=\"h-px w-6 bg-gray-200 dark:bg-neutral-700\" />\n              <div className=\"flex items-center gap-2\">\n                <span className=\"relative flex h-2 w-2 flex-shrink-0\">\n                  <span className=\"animate-ping absolute inline-flex h-full w-full rounded-full bg-emerald-400 opacity-60\" />\n                  <span className=\"relative inline-flex rounded-full h-2 w-2 bg-emerald-500\" />\n                </span>\n                <span className=\"text-sm font-semibold text-gray-900 dark:text-white font-mono\">\n                  {visitors?.totalVisitors ?? \"—\"}\n                </span>\n              </div>\n            </div>\n          </motion.div>\n\n          {/* Now Playing */}\n          <motion.div\n            initial={{ opacity: 0, y: 16 }}\n            whileInView={{ opacity: 1, y: 0 }}\n            viewport={{ once: true }}\n            transition={{ delay: 0.1 }}\n          >\n            <span className=\"font-mono text-[9px] tracking-[0.4em] uppercase text-gray-500 dark:text-gray-500 mb-4 block\">\n              Now Playing\n            </span>\n            {currentSong?.isPlaying ? (\n              <WhenPlaying song={currentSong} />\n            ) : (\n              <NotPlaying />\n            )}\n          </motion.div>\n        </div>\n\n        {/* ── Divider ── */}\n        <div className=\"h-px bg-gray-200 dark:bg-neutral-700 mb-10\" />\n\n        {/* ── Links grid ── */}\n        <div className=\"grid grid-cols-2 md:grid-cols-3 gap-x-12 gap-y-8 mb-10\">\n          {/* Navigation */}\n          <motion.div\n            initial={{ opacity: 0, y: 12 }}\n            whileInView={{ opacity: 1, y: 0 }}\n            viewport={{ once: true }}\n            transition={{ delay: 0.1 }}\n          >\n            <span className=\"font-mono text-[9px] tracking-[0.4em] uppercase text-gray-500 dark:text-gray-500 mb-3 block\">\n              Navigation\n            </span>\n            <div className=\"divide-y divide-gray-200 dark:divide-neutral-700\">\n              {navLinks.map((text, i) => (\n                <FooterLink\n                  key={i}\n                  href={`/${text}`}\n                  text={text}\n                  isExternal={false}\n                />\n              ))}\n            </div>\n          </motion.div>\n\n          {/* More */}\n          <motion.div\n            initial={{ opacity: 0, y: 12 }}\n            whileInView={{ opacity: 1, y: 0 }}\n            viewport={{ once: true }}\n            transition={{ delay: 0.15 }}\n          >\n            <span className=\"font-mono text-[9px] tracking-[0.4em] uppercase text-gray-500 dark:text-gray-500 mb-3 block\">\n              More\n            </span>\n            <div className=\"divide-y divide-gray-200 dark:divide-neutral-700\">\n              {moreLinks.map((route, i) => (\n                <FooterLink\n                  key={i}\n                  href={`/${route}`}\n                  text={route === \"rss\" ? \"RSS\" : route}\n                  isExternal={false}\n                />\n              ))}\n            </div>\n          </motion.div>\n\n          {/* Connect */}\n          <motion.div\n            initial={{ opacity: 0, y: 12 }}\n            whileInView={{ opacity: 1, y: 0 }}\n            viewport={{ once: true }}\n            transition={{ delay: 0.2 }}\n          >\n            <span className=\"font-mono text-[9px] tracking-[0.4em] uppercase text-gray-500 dark:text-gray-500 mb-3 block\">\n              Connect\n            </span>\n            <div className=\"divide-y divide-gray-200 dark:divide-neutral-700\">\n              {socialMedia.slice(0, 5).map((platform, i) => (\n                <FooterLink\n                  key={i}\n                  href={platform.url}\n                  text={platform.title}\n                  isExternal={true}\n                />\n              ))}\n            </div>\n          </motion.div>\n        </div>\n\n        {/* ── Bottom bar ── */}\n        <div className=\"h-px bg-gray-200 dark:bg-neutral-700 mb-8\" />\n        <motion.div\n          initial={{ opacity: 0 }}\n          whileInView={{ opacity: 1 }}\n          viewport={{ once: true }}\n          className=\"flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4\"\n        >\n          <div className=\"flex flex-wrap items-center gap-x-3 gap-y-1\">\n            <span className=\"font-mono text-[10px] tracking-[0.3em] uppercase text-gray-600 dark:text-gray-400\">\n              © {new Date().getFullYear()} Jatin Sharma\n            </span>\n            <span className=\"text-gray-300 dark:text-gray-700 text-xs hidden sm:inline\">\n              ·\n            </span>\n            <span className=\"font-mono text-[10px] tracking-[0.2em] uppercase text-gray-500 dark:text-gray-500\">\n              Built with{\" \"}\n              <Link\n                href=\"https://nextjs.org\"\n                target=\"_blank\"\n                rel=\"noreferrer\"\n                className=\"text-neutral-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white transition-colors\"\n              >\n                Next.js\n              </Link>{\" \"}\n              &{\" \"}\n              <Link\n                href=\"https://vercel.com\"\n                target=\"_blank\"\n                rel=\"noreferrer\"\n                className=\"text-neutral-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white transition-colors\"\n              >\n                Vercel\n              </Link>\n            </span>\n          </div>\n\n          {/* QR button — matches contact CTA invert style */}\n          <motion.button\n            whileTap={{ scale: 0.96 }}\n            onClick={() => setShowQR(!showQR)}\n            className=\"group relative inline-flex items-center gap-2 px-4 py-2 border border-gray-900 dark:border-white text-gray-900 dark:text-white text-sm font-semibold overflow-hidden hover:text-white dark:hover:text-gray-900 focus:outline-none transition-colors duration-300\"\n            aria-label=\"Show QR Code\"\n          >\n            <span className=\"absolute inset-0 bg-gray-900 dark:bg-white translate-y-full group-hover:translate-y-0 transition-transform duration-300 ease-[cubic-bezier(0.22,1,0.36,1)]\" />\n            <span className=\"relative z-10 flex items-center gap-2\">\n              <HiOutlineQrcode className=\"w-4 h-4\" />\n              QR Code\n            </span>\n          </motion.button>\n        </motion.div>\n      </div>\n    </footer>\n  );\n}\n\nfunction FooterLink({\n  href,\n  text,\n  isExternal,\n}: {\n  href: string;\n  text: string;\n  isExternal: boolean;\n}) {\n  return (\n    <Link\n      href={href}\n      target={isExternal ? \"_blank\" : undefined}\n      rel={isExternal ? \"noopener noreferrer\" : undefined}\n      className=\"flex items-center justify-between py-2.5 group text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white transition-colors capitalize\"\n    >\n      <span className=\"relative\">\n        <span className=\"absolute bottom-0 left-0 h-px w-0 group-hover:w-full bg-current transition-all duration-300\" />\n        {text}\n      </span>\n      <span className=\"opacity-0 group-hover:opacity-100 transition-opacity text-xs\">\n        {isExternal ? \"↗\" : \"→\"}\n      </span>\n    </Link>\n  );\n}\n\nfunction NotPlaying() {\n  return (\n    <div className=\"flex items-center gap-3 py-1\">\n      <SiSpotify className=\"w-5 h-5 text-gray-400 dark:text-gray-600 flex-shrink-0\" />\n      <div>\n        <p className=\"text-sm font-medium text-gray-700 dark:text-gray-300\">\n          Not Playing\n        </p>\n        <p className=\"text-xs font-mono text-gray-500 dark:text-gray-500\">\n          Spotify\n        </p>\n      </div>\n    </div>\n  );\n}\n\nfunction WhenPlaying({ song }: { song: Song }) {\n  return (\n    <Link\n      href={song.songUrl}\n      target=\"_blank\"\n      rel=\"noopener noreferrer\"\n      className=\"group flex items-center gap-4 py-1\"\n    >\n      <div className=\"relative flex-shrink-0\">\n        <Image\n          alt={song.title}\n          src={song.albumImageUrl}\n          width={40}\n          height={40}\n          quality={50}\n          className=\"rounded-sm grayscale group-hover:grayscale-0 transition-all duration-500\"\n        />\n      </div>\n      <div className=\"flex-1 min-w-0\">\n        <p className=\"text-sm font-semibold text-gray-900 dark:text-white truncate group-hover:underline underline-offset-2 decoration-gray-400\">\n          {song.title}\n        </p>\n        <p className=\"text-xs text-gray-500 dark:text-gray-400 font-mono truncate mt-0.5\">\n          {song.artist}\n        </p>\n      </div>\n      {/* Equaliser bars */}\n      <div className=\"flex items-end gap-0.5 flex-shrink-0 h-5\">\n        {[3, 4, 2, 5].map((h, i) => (\n          <motion.span\n            key={i}\n            animate={{ height: [`${h * 3}px`, `${h * 4.5}px`, `${h * 3}px`] }}\n            transition={{\n              duration: 0.8,\n              repeat: Infinity,\n              delay: i * 0.15,\n              ease: \"easeInOut\",\n            }}\n            className=\"w-0.5 bg-emerald-500 rounded-full inline-block\"\n          />\n        ))}\n      </div>\n    </Link>\n  );\n}\n"
  },
  {
    "path": "components/FramerMotion/AnimatedDiv.tsx",
    "content": "import { AnimatedTAGProps } from \"@lib/types\";\nimport { motion } from \"framer-motion\";\n\nexport default function AnimatedDiv({\n  variants,\n  className,\n  children,\n  infinity,\n}: AnimatedTAGProps) {\n  return (\n    <motion.div\n      initial=\"hidden\"\n      whileInView=\"visible\"\n      viewport={{ once: !infinity }}\n      variants={variants}\n      className={className}\n      // style={style}\n      transition={{ staggerChildren: 0.5 }}\n    >\n      {children}\n    </motion.div>\n  );\n}\n"
  },
  {
    "path": "components/FramerMotion/AnimatedHeading.tsx",
    "content": "import { motion } from \"framer-motion\";\nimport { AnimatedTAGProps } from \"@lib/types\";\n\nexport default function AnimatedHeading({\n  variants,\n  className,\n  children,\n  infinity,\n}: AnimatedTAGProps) {\n  return (\n    <motion.h1\n      initial=\"hidden\"\n      whileInView=\"visible\"\n      viewport={{ once: !infinity }}\n      variants={variants}\n      className={className}\n    >\n      {children}\n    </motion.h1>\n  );\n}\n"
  },
  {
    "path": "components/FramerMotion/AnimatedText.tsx",
    "content": "import { AnimatedTAGProps } from \"@lib/types\";\nimport { motion } from \"framer-motion\";\n\nexport default function AnimatedText({\n  variants,\n  className,\n  children,\n  infinity,\n}: AnimatedTAGProps) {\n  return (\n    <motion.p\n      initial=\"hidden\"\n      whileInView=\"visible\"\n      viewport={{ once: !infinity }}\n      variants={variants}\n      className={className}\n    >\n      {children}\n    </motion.p>\n  );\n}\n"
  },
  {
    "path": "components/GitHubActivityGraph.tsx",
    "content": "import {\n  Area,\n  AreaChart,\n  Bar,\n  BarChart,\n  CartesianGrid,\n  ResponsiveContainer,\n  Tooltip,\n  TooltipProps,\n  XAxis,\n  YAxis,\n} from \"recharts\";\nimport {\n  NameType,\n  ValueType,\n} from \"recharts/types/component/DefaultTooltipContent\";\n\nimport { motion } from \"framer-motion\";\nimport React from \"react\";\nimport fetcher from \"@lib/fetcher\";\nimport { getFormattedDate } from \"@utils/date\";\nimport { useDarkMode } from \"@context/darkModeContext\";\nimport useSWR from \"swr\";\n\nfunction ChartSectionHeading({\n  title,\n  description,\n}: {\n  title: string;\n  description: string;\n}) {\n  return (\n    <div className=\"mb-6 space-y-2\">\n      <motion.div\n        initial={{ opacity: 0, x: -16 }}\n        whileInView={{ opacity: 1, x: 0 }}\n        viewport={{ once: true }}\n        className=\"flex items-center gap-3\"\n      >\n        <div className=\"h-px w-5 bg-gray-400 dark:bg-gray-600 flex-shrink-0\" />\n        <span className=\"font-mono text-[10px] tracking-[0.45em] uppercase text-gray-500 dark:text-gray-500\">\n          Activity\n        </span>\n      </motion.div>\n      <motion.h3\n        initial={{ opacity: 0, y: 12 }}\n        whileInView={{ opacity: 1, y: 0 }}\n        viewport={{ once: true }}\n        transition={{ delay: 0.06 }}\n        className=\"text-xl sm:text-2xl font-bold text-gray-900 dark:text-white\"\n      >\n        {title}\n      </motion.h3>\n      <motion.p\n        initial={{ opacity: 0, y: 8 }}\n        whileInView={{ opacity: 1, y: 0 }}\n        viewport={{ once: true }}\n        transition={{ delay: 0.12 }}\n        className=\"text-sm text-gray-600 dark:text-gray-400\"\n      >\n        {description}\n      </motion.p>\n    </div>\n  );\n}\n\nexport default function GitHubActivityGraph() {\n  const { isDarkMode } = useDarkMode();\n  const { data: githubActivity } = useSWR(\n    \"/api/stats/github-contribution\",\n    fetcher,\n  );\n\n  return (\n    <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-8\">\n      <div className=\"max-w-full\">\n        <ChartSectionHeading\n          title=\"Activity Graph\"\n          description=\"GitHub contributions over the last 31 days.\"\n        />\n        <ResponsiveContainer width=\"100%\" height={300}>\n          {githubActivity?.contributions ? (\n            <AreaChart\n              width={730}\n              height={250}\n              data={githubActivity?.contributions}\n              margin={{ top: 10, right: 30, left: 0, bottom: 0 }}\n            >\n              <defs>\n                <linearGradient id=\"colorUv\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n                  <stop\n                    offset=\"5%\"\n                    stopColor={isDarkMode ? \"#26a64160\" : \"#26a641\"}\n                    stopOpacity={0.8}\n                  />\n                  <stop\n                    offset=\"95%\"\n                    stopColor={isDarkMode ? \"#26a64160\" : \"#26a641\"}\n                    stopOpacity={0}\n                  />\n                </linearGradient>\n              </defs>\n              <XAxis dataKey=\"shortDate\" />\n              <YAxis />\n              <CartesianGrid\n                strokeDasharray=\"2 3\"\n                stroke={isDarkMode ? \"#ffffff20\" : \"#00000020\"}\n              />\n              <Tooltip content={<ContributionsToolTip />} />\n              <Area\n                dot\n                activeDot\n                strokeWidth={3}\n                type=\"monotone\"\n                dataKey=\"contributionCount\"\n                aria-label=\"count\"\n                stroke={isDarkMode ? \"#26a641\" : \"#216e39\"}\n                fillOpacity={1}\n                fill=\"url(#colorUv)\"\n              />\n            </AreaChart>\n          ) : (\n            <LoadingAreaChart />\n          )}\n        </ResponsiveContainer>\n      </div>\n\n      <div className=\"max-w-full\">\n        <ChartSectionHeading\n          title=\"Productivity by Day\"\n          description=\"Contributions by day of the week.\"\n        />\n        <ResponsiveContainer width=\"100%\" height={300}>\n          {githubActivity?.contributionCountByDayOfWeek ? (\n            <BarChart\n              width={730}\n              height={250}\n              data={githubActivity?.contributionCountByDayOfWeek}\n              margin={{ top: 10, right: 30, left: 0, bottom: 0 }}\n            >\n              <CartesianGrid\n                strokeDasharray=\"2 3\"\n                stroke={isDarkMode ? \"#ffffff20\" : \"#00000020\"}\n              />\n              <XAxis dataKey=\"day\" />\n              <YAxis />\n              <Tooltip content={<ContributionCountByDayOfWeekToolTip />} />\n              <Bar dataKey=\"count\" fill=\"#26a641\" />\n            </BarChart>\n          ) : (\n            <LoadingBarChart />\n          )}\n        </ResponsiveContainer>\n      </div>\n    </div>\n  );\n}\n\nconst ContributionsToolTip = ({\n  active,\n  payload,\n}: TooltipProps<ValueType, NameType>) => {\n  if (active && payload && payload.length) {\n    return (\n      <div className=\"p-5 rounded-md bg-white dark:bg-darkSecondary text-black dark:text-gray-200 text-sm max-w-[250px] w-fit shadow-lg\">\n        <p className=\"label\">\n          <span className=\"font-medium\">Date :</span>{\" \"}\n          {getFormattedDate(new Date(payload[0].payload.date))}\n        </p>\n        <p className=\"desc\">\n          <span className=\"font-medium\">Commit Count :</span> {payload[0].value}\n        </p>\n      </div>\n    );\n  }\n\n  return null;\n};\n\nconst ContributionCountByDayOfWeekToolTip = ({\n  active,\n  payload,\n}: TooltipProps<ValueType, NameType>) => {\n  if (active && payload && payload.length) {\n    return (\n      <div className=\"p-5 rounded-md bg-white dark:bg-darkSecondary text-black dark:text-gray-200 text-sm max-w-[250px] w-fit shadow-lg\">\n        <p className=\"label\">\n          <span className=\"font-medium\">Day :</span> {payload[0].payload.day}\n        </p>\n        <p className=\"desc\">\n          <span className=\"font-medium\">Commit Count :</span> {payload[0].value}\n        </p>\n      </div>\n    );\n  }\n\n  return null;\n};\n\nfunction LoadingBarChart() {\n  const { isDarkMode } = useDarkMode();\n\n  const barGraphLoadingData = [\n    { day: \"Monday\", count: 3 },\n    { day: \"Tuesday\", count: 5 },\n    { day: \"Wednesday\", count: 7 },\n    { day: \"Thursday\", count: 9 },\n    { day: \"Friday\", count: 11 },\n    { day: \"Saturday\", count: 13 },\n    { day: \"Sunday\", count: 15 },\n  ];\n\n  return (\n    <div className=\"pointer-events-none relative\">\n      <div className=\"grid place-items-center font-semibold text-base sm:text-lg absolute inset-0 z-1\">\n        Loading Data...\n      </div>\n      <BarChart\n        width={730}\n        height={250}\n        data={barGraphLoadingData}\n        margin={{ top: 10, right: 30, left: 0, bottom: 0 }}\n        style={{\n          opacity: \"0.25\",\n        }}\n      >\n        <CartesianGrid\n          strokeDasharray=\"2 3\"\n          stroke={isDarkMode ? \"#ffffff20\" : \"#00000020\"}\n        />\n        <XAxis dataKey=\"day\" />\n        <YAxis />\n\n        <Tooltip\n          cursor={{ fill: \"transparent\" }}\n          content={<ContributionCountByDayOfWeekToolTip />}\n        />\n        <Bar dataKey=\"count\" fill={isDarkMode ? \"#404040\" : \"#ababab\"} />\n      </BarChart>\n    </div>\n  );\n}\n\nfunction LoadingAreaChart() {\n  const { isDarkMode } = useDarkMode();\n\n  const areaChartLoadingData = [\n    { shortDate: \"09\", contributionCount: 3 },\n    { shortDate: \"10\", contributionCount: 5 },\n    { shortDate: \"11\", contributionCount: 15 },\n    { shortDate: \"12\", contributionCount: 9 },\n    { shortDate: \"13\", contributionCount: 4 },\n    { shortDate: \"14\", contributionCount: 13 },\n    { shortDate: \"15\", contributionCount: 5 },\n    { shortDate: \"16\", contributionCount: 4 },\n    { shortDate: \"17\", contributionCount: 9 },\n    { shortDate: \"18\", contributionCount: 2 },\n    { shortDate: \"19\", contributionCount: 5 },\n    { shortDate: \"21\", contributionCount: 6 },\n    { shortDate: \"22\", contributionCount: 7 },\n    { shortDate: \"23\", contributionCount: 3 },\n    { shortDate: \"24\", contributionCount: 21 },\n    { shortDate: \"25\", contributionCount: 4 },\n    { shortDate: \"26\", contributionCount: 9 },\n    { shortDate: \"27\", contributionCount: 2 },\n  ];\n\n  return (\n    <div className=\"pointer-events-none relative\">\n      <div className=\"grid place-items-center font-semibold text-base sm:text-lg absolute inset-0 z-1\">\n        Loading Data...\n      </div>\n      <AreaChart\n        width={730}\n        height={250}\n        data={areaChartLoadingData}\n        margin={{ top: 10, right: 30, left: 0, bottom: 0 }}\n        style={{\n          opacity: \"0.25\",\n        }}\n      >\n        <defs>\n          <linearGradient id=\"colorUv\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n            <stop\n              offset=\"5%\"\n              stopColor={isDarkMode ? \"#404040\" : \"#ababab\"}\n              stopOpacity={0.8}\n            />\n            <stop\n              offset=\"95%\"\n              stopColor={isDarkMode ? \"#404040\" : \"#ababab\"}\n              stopOpacity={0}\n            />\n          </linearGradient>\n        </defs>\n        <XAxis dataKey=\"shortDate\" />\n        <YAxis />\n        <CartesianGrid\n          strokeDasharray=\"2 3\"\n          stroke={isDarkMode ? \"#ffffff20\" : \"#00000020\"}\n        />\n        <Tooltip content={<ContributionsToolTip />} />\n        <Area\n          dot\n          activeDot\n          strokeWidth={3}\n          type=\"monotone\"\n          dataKey=\"contributionCount\"\n          aria-label=\"count\"\n          stroke={isDarkMode ? \"#404040\" : \"#ababab\"}\n          fillOpacity={1}\n          fill=\"url(#colorUv)\"\n        />\n      </AreaChart>\n    </div>\n  );\n}\n"
  },
  {
    "path": "components/Home/BlogsSection.tsx",
    "content": "import Blog from \"../Blog\";\nimport { BlogPost } from \"@lib/interface/sanity\";\nimport Link from \"next/link\";\nimport { motion } from \"framer-motion\";\nimport { FiArrowRight } from \"react-icons/fi\";\nimport siteConfig from \"@content/siteConfig\";\n\nconst containerVariants = {\n  hidden: {},\n  visible: { transition: { staggerChildren: 0.1 } },\n};\n\nexport default function BlogsSection({\n  blogs,\n  totalBlogs,\n}: {\n  blogs: BlogPost[];\n  totalBlogs: number;\n}) {\n  const { blogsSection } = siteConfig.home;\n\n  return (\n    <section className=\"pt-20 sm:pt-24 relative overflow-hidden\">\n      {/* Section number watermark */}\n      <div\n        className=\"absolute -right-2 top-6 font-black select-none pointer-events-none leading-none tracking-tighter bg-gradient-to-b from-gray-200 to-gray-50 dark:from-[#232628] dark:to-darkPrimary bg-clip-text text-transparent\"\n        style={{ fontSize: \"clamp(5rem, 16vw, 13rem)\" }}\n        aria-hidden=\"true\"\n      >\n        03\n      </div>\n\n      {/* ── Header ── */}\n      <div className=\"relative z-10 flex flex-col lg:flex-row lg:items-end lg:justify-between gap-8 mb-12\">\n        <div className=\"space-y-3 max-w-xl\">\n          {/* Section label */}\n          <motion.div\n            initial={{ opacity: 0, x: -16 }}\n            whileInView={{ opacity: 1, x: 0 }}\n            viewport={{ once: true }}\n            className=\"flex items-center gap-3\"\n          >\n            <div className=\"h-px w-5 bg-gray-400 dark:bg-gray-600 flex-shrink-0\" />\n            <span className=\"font-mono text-[10px] tracking-[0.45em] uppercase text-gray-400 dark:text-gray-600\">\n              {blogsSection.eyebrow}\n            </span>\n          </motion.div>\n\n          <motion.h2\n            initial={{ opacity: 0, y: 20 }}\n            whileInView={{ opacity: 1, y: 0 }}\n            viewport={{ once: true }}\n            transition={{ delay: 0.1 }}\n            className=\"text-3xl sm:text-4xl lg:text-5xl font-bold text-gray-900 dark:text-white\"\n          >\n            {blogsSection.title}\n          </motion.h2>\n\n          <motion.p\n            initial={{ opacity: 0, y: 16 }}\n            whileInView={{ opacity: 1, y: 0 }}\n            viewport={{ once: true }}\n            transition={{ delay: 0.15 }}\n            className=\"text-base text-gray-600 dark:text-gray-400 border-l-2 border-gray-300 dark:border-gray-700 pl-4 py-0.5\"\n          >\n            {blogsSection.description}\n          </motion.p>\n        </div>\n\n        {/* Article count + CTA */}\n        <motion.div\n          initial={{ opacity: 0, x: 20 }}\n          whileInView={{ opacity: 1, x: 0 }}\n          viewport={{ once: true }}\n          transition={{ delay: 0.15 }}\n          className=\"flex flex-col items-start lg:items-end gap-3 flex-shrink-0\"\n        >\n          <div className=\"flex items-end gap-2\">\n            <span className=\"text-5xl sm:text-6xl font-black text-gray-900 dark:text-white leading-none\">\n              {totalBlogs}\n            </span>\n            <span className=\"text-xs font-mono uppercase tracking-widest text-gray-500 dark:text-gray-500 mb-2\">\n              Articles\n            </span>\n          </div>\n          <Link\n            href=\"/blogs\"\n            className=\"group inline-flex items-center gap-2 text-[10px] font-mono uppercase tracking-[0.25em] text-gray-500 dark:text-gray-500 hover:text-gray-900 dark:hover:text-white transition-colors\"\n          >\n            {blogsSection.ctaLabel}\n            <FiArrowRight className=\"w-3 h-3 group-hover:translate-x-1 transition-transform\" />\n          </Link>\n        </motion.div>\n      </div>\n\n      {/* ── Blog list ── */}\n      <motion.div\n        variants={containerVariants}\n        initial=\"hidden\"\n        whileInView=\"visible\"\n        viewport={{ once: true }}\n        className=\"flex flex-col\"\n      >\n        {blogs.map((blog, index) => (\n          <Blog key={`home-blog-${index}`} blog={blog} index={index} />\n        ))}\n      </motion.div>\n    </section>\n  );\n}\n"
  },
  {
    "path": "components/Home/EpigraphsSection.tsx",
    "content": "import { IEpigraph } from \"@lib/interface/sanity\";\nimport Link from \"next/link\";\nimport EpigraphCard from \"@components/EpigraphCard\";\nimport { motion } from \"framer-motion\";\nimport { FiArrowRight } from \"react-icons/fi\";\nimport siteConfig from \"@content/siteConfig\";\n\nconst containerVariants = {\n  hidden: {},\n  visible: { transition: { staggerChildren: 0.08 } },\n};\n\nexport default function EpigraphsSection({\n  epigraphs,\n  totalEpigraphs,\n}: {\n  epigraphs: IEpigraph[];\n  totalEpigraphs: number;\n}) {\n  const { epigraphsSection } = siteConfig.home;\n\n  return (\n    <section className=\"pt-20 sm:pt-24 relative overflow-hidden\">\n      {/* Section number watermark */}\n      <div\n        className=\"absolute -right-2 top-6 font-black select-none pointer-events-none leading-none tracking-tighter bg-gradient-to-b from-gray-200 to-gray-50 dark:from-[#232628] dark:to-darkPrimary bg-clip-text text-transparent\"\n        style={{ fontSize: \"clamp(5rem, 16vw, 13rem)\" }}\n        aria-hidden=\"true\"\n      >\n        04\n      </div>\n\n      {/* ── Header ── */}\n      <div className=\"relative z-10 flex flex-col lg:flex-row lg:items-end lg:justify-between gap-8 mb-12\">\n        <div className=\"space-y-3 max-w-xl\">\n          {/* Section label */}\n          <motion.div\n            initial={{ opacity: 0, x: -16 }}\n            whileInView={{ opacity: 1, x: 0 }}\n            viewport={{ once: true }}\n            className=\"flex items-center gap-3\"\n          >\n            <div className=\"h-px w-5 bg-gray-400 dark:bg-gray-600 flex-shrink-0\" />\n            <span className=\"font-mono text-[10px] tracking-[0.45em] uppercase text-gray-400 dark:text-gray-600\">\n              {epigraphsSection.eyebrow}\n            </span>\n          </motion.div>\n\n          <motion.h2\n            initial={{ opacity: 0, y: 20 }}\n            whileInView={{ opacity: 1, y: 0 }}\n            viewport={{ once: true }}\n            transition={{ delay: 0.1 }}\n            className=\"text-3xl sm:text-4xl lg:text-5xl font-bold text-gray-900 dark:text-white\"\n          >\n            {epigraphsSection.title}\n          </motion.h2>\n\n          <motion.p\n            initial={{ opacity: 0, y: 16 }}\n            whileInView={{ opacity: 1, y: 0 }}\n            viewport={{ once: true }}\n            transition={{ delay: 0.15 }}\n            className=\"text-base text-gray-600 dark:text-gray-400 border-l-2 border-gray-300 dark:border-gray-700 pl-4 py-0.5\"\n          >\n            {epigraphsSection.description}\n          </motion.p>\n        </div>\n\n        {/* Count + CTA */}\n        <motion.div\n          initial={{ opacity: 0, x: 20 }}\n          whileInView={{ opacity: 1, x: 0 }}\n          viewport={{ once: true }}\n          transition={{ delay: 0.15 }}\n          className=\"flex flex-col items-start lg:items-end gap-3 flex-shrink-0\"\n        >\n          <div className=\"flex items-end gap-2\">\n            <span className=\"text-5xl sm:text-6xl font-black text-gray-900 dark:text-white leading-none\">\n              {totalEpigraphs}\n            </span>\n            <span className=\"text-xs font-mono uppercase tracking-widest text-gray-500 dark:text-gray-500 mb-2\">\n              Epigraphs\n            </span>\n          </div>\n          <Link\n            href=\"/epigraphs\"\n            className=\"group inline-flex items-center gap-2 text-[10px] font-mono uppercase tracking-[0.25em] text-gray-500 dark:text-gray-500 hover:text-gray-900 dark:hover:text-white transition-colors\"\n          >\n            {epigraphsSection.ctaLabel}\n            <FiArrowRight className=\"w-3 h-3 group-hover:translate-x-1 transition-transform\" />\n          </Link>\n        </motion.div>\n      </div>\n\n      {/* ── Epigraphs list ── */}\n      <motion.div\n        variants={containerVariants}\n        initial=\"hidden\"\n        whileInView=\"visible\"\n        viewport={{ once: true }}\n        className=\"divide-y divide-gray-100 dark:divide-neutral-800\"\n      >\n        {epigraphs.map((epigraph, i) => (\n          <EpigraphCard key={epigraph._id} epigraph={epigraph} index={i} />\n        ))}\n      </motion.div>\n    </section>\n  );\n}\n"
  },
  {
    "path": "components/Home/HeroSection.tsx",
    "content": "import React from \"react\";\nimport Image from \"next/image\";\nimport Link from \"next/link\";\nimport { motion } from \"framer-motion\";\nimport {\n  FiDownload,\n  FiArrowRight,\n  FiGithub,\n  FiLinkedin,\n  FiTwitter,\n} from \"react-icons/fi\";\nimport { opacityVariant, popUp } from \"@content/FramerMotionVariants\";\nimport { featuredSocialLinks } from \"@content/siteConfig\";\nimport siteConfig from \"@content/siteConfig\";\n\nconst socialIconMap = {\n  github: FiGithub,\n  linkedin: FiLinkedin,\n  twitter: FiTwitter,\n};\n\nexport default function HeroSection() {\n  const { hero } = siteConfig.home;\n\n  return (\n    <section className=\"relative min-h-screen flex items-center py-24 lg:py-0 px-6 sm:px-8 lg:px-12 overflow-hidden\">\n      {/* ── Dot grid ── */}\n      <div\n        className=\"absolute inset-0 pointer-events-none select-none\"\n        style={{\n          backgroundImage:\n            \"radial-gradient(circle, #6b7280 1px, transparent 1px)\",\n          backgroundSize: \"28px 28px\",\n          opacity: 0.12,\n        }}\n        aria-hidden=\"true\"\n      />\n\n      {/* ── Diagonal stripe — right half ── */}\n      <div\n        className=\"absolute top-0 right-0 w-1/2 h-full hero-stripe pointer-events-none\"\n        aria-hidden=\"true\"\n      />\n\n      {/* ── Bottom fade ── */}\n      <div className=\"absolute bottom-0 inset-x-0 h-56 bg-gradient-to-t from-white dark:from-darkPrimary to-transparent pointer-events-none\" />\n\n      {/* ── JS initials — gradient emboss ── */}\n      <div\n        className=\"absolute -right-4 top-1/2 -translate-y-1/2 font-black select-none pointer-events-none leading-none tracking-tighter bg-gradient-to-b from-gray-200 to-gray-50 dark:from-[#232628] dark:to-darkPrimary bg-clip-text text-transparent\"\n        style={{ fontSize: \"clamp(8rem, 24vw, 22rem)\" }}\n        aria-hidden=\"true\"\n      >\n        JS\n      </div>\n\n      <div className=\"relative max-w-7xl mx-auto w-full z-10\">\n        {/* ── Corner cross-tick marks ── */}\n        {(\n          [\n            \"top-0 left-0\",\n            \"top-0 right-0\",\n            \"bottom-8 left-0\",\n            \"bottom-8 right-0\",\n          ] as const\n        ).map((pos) => (\n          <div\n            key={pos}\n            className={`absolute ${pos} w-5 h-5 pointer-events-none`}\n            aria-hidden=\"true\"\n          >\n            <div className=\"absolute top-1/2 -translate-y-1/2 w-full h-px bg-gray-300 dark:bg-gray-700\" />\n            <div className=\"absolute left-1/2 -translate-x-1/2 h-full w-px bg-gray-300 dark:bg-gray-700\" />\n          </div>\n        ))}\n\n        <div className=\"grid lg:grid-cols-5 gap-12 lg:gap-8 items-center py-16 lg:py-20\">\n          {/* ── Left content (3 cols) ── */}\n          <div className=\"lg:col-span-3 flex flex-col gap-7 text-center lg:text-left\">\n            {/* Availability badge — inverted */}\n            <motion.div variants={opacityVariant}>\n              <span className=\"inline-flex items-center gap-2 px-4 py-1.5 rounded-full bg-gray-900 dark:bg-white text-[11px] font-semibold tracking-[0.1em] uppercase text-white dark:text-gray-900\">\n                <span className=\"w-1.5 h-1.5 rounded-full bg-emerald-400 animate-pulse flex-shrink-0\" />\n                {hero.availabilityBadge}\n              </span>\n            </motion.div>\n\n            {/* Name */}\n            <div>\n              <motion.p\n                variants={opacityVariant}\n                className=\"text-xs sm:text-sm font-light tracking-[0.5em] uppercase text-gray-500 dark:text-gray-500 mb-4\"\n              >\n                {hero.greeting}\n              </motion.p>\n              <motion.h1\n                variants={opacityVariant}\n                className=\"font-black leading-[0.9] tracking-tight\"\n                style={{ fontSize: \"clamp(3.2rem, 9vw, 6.5rem)\" }}\n              >\n                <span className=\"text-neutral-700 dark:text-gray-200\">\n                  Jatin\n                </span>\n                <br />\n                <span className=\"font-sarina font-normal text-gray-900 dark:text-white\">\n                  Sharma\n                </span>\n              </motion.h1>\n            </div>\n\n            {/* Role line */}\n            <motion.div\n              variants={opacityVariant}\n              className=\"flex items-center gap-4 justify-center lg:justify-start\"\n            >\n              <div className=\"h-px w-8 bg-gray-400 dark:bg-gray-600 flex-shrink-0\" />\n              <p className=\"text-xs font-mono uppercase tracking-widest text-gray-500 dark:text-gray-500 whitespace-nowrap\">\n                {hero.rolePrefix}{\" \"}\n                <Link\n                  href={hero.companyUrl}\n                  target=\"_blank\"\n                  rel=\"noopener noreferrer\"\n                  className=\"text-neutral-700 dark:text-gray-200 font-semibold hover:underline underline-offset-4 decoration-gray-400 dark:decoration-gray-600\"\n                >\n                  {hero.companyName}\n                </Link>\n              </p>\n              <div className=\"h-px flex-1 bg-gray-200 dark:bg-neutral-700 hidden sm:block\" />\n            </motion.div>\n\n            {/* Bio — left accent border */}\n            <motion.p\n              variants={opacityVariant}\n              className=\"text-base sm:text-lg text-gray-600 dark:text-gray-400 leading-relaxed max-w-lg mx-auto lg:mx-0 border-l-2 border-gray-300 dark:border-gray-700 pl-5 py-1\"\n            >\n              {hero.roleSuffix.replace(/^\\.\\s*/, \"\")}\n            </motion.p>\n\n            {/* CTAs */}\n            <motion.div\n              variants={opacityVariant}\n              className=\"flex flex-col sm:flex-row gap-3 justify-center lg:justify-start\"\n            >\n              {/* Primary — solid fill */}\n              <Link\n                href={hero.primaryCta.url}\n                target=\"_blank\"\n                rel=\"noopener noreferrer\"\n                className=\"group relative inline-flex items-center justify-center gap-2 px-7 py-3.5 bg-gray-900 dark:bg-white text-white dark:text-gray-900 font-semibold text-sm overflow-hidden transition-colors duration-300 hover:bg-neutral-700 dark:hover:bg-gray-100 active:scale-95\"\n              >\n                <FiDownload className=\"w-4 h-4 transition-transform group-hover:-translate-y-0.5 duration-200 relative z-10\" />\n                <span className=\"relative z-10\">{hero.primaryCta.label}</span>\n              </Link>\n\n              {/* Secondary — invert-fill on hover */}\n              <Link\n                href={hero.secondaryCta.url}\n                className=\"group relative inline-flex items-center justify-center gap-2 px-7 py-3.5 border border-gray-400 dark:border-gray-600 text-gray-900 dark:text-white font-semibold text-sm overflow-hidden hover:text-white dark:hover:text-gray-900 transition-colors duration-300 active:scale-95\"\n              >\n                <span className=\"absolute inset-0 bg-gray-900 dark:bg-white translate-y-full group-hover:translate-y-0 transition-transform duration-300 ease-[cubic-bezier(0.22,1,0.36,1)]\" />\n                <span className=\"relative z-10\">{hero.secondaryCta.label}</span>\n                <FiArrowRight className=\"w-4 h-4 relative z-10 transition-transform group-hover:translate-x-0.5 duration-200\" />\n              </Link>\n            </motion.div>\n\n            {/* Social links */}\n            <motion.div\n              variants={opacityVariant}\n              className=\"flex items-center gap-3 justify-center lg:justify-start\"\n            >\n              <span className=\"text-[10px] font-mono uppercase tracking-[0.3em] text-gray-500 dark:text-gray-500\">\n                {hero.socialLabel}\n              </span>\n              <div className=\"w-px h-4 bg-gray-300 dark:bg-gray-700 flex-shrink-0\" />\n              {featuredSocialLinks.map((socialLink) => {\n                const Icon =\n                  socialIconMap[socialLink.icon as keyof typeof socialIconMap];\n                if (!Icon) return null;\n                return (\n                  <a\n                    key={socialLink.title}\n                    href={socialLink.url}\n                    target=\"_blank\"\n                    rel=\"noopener noreferrer\"\n                    aria-label={socialLink.title}\n                    className=\"p-2 text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white border border-gray-300 dark:border-gray-700 hover:border-gray-700 dark:hover:border-gray-400 hover:bg-gray-50 dark:hover:bg-darkSecondary transition-all\"\n                  >\n                    <Icon className=\"w-5 h-5\" />\n                  </a>\n                );\n              })}\n            </motion.div>\n          </div>\n\n          {/* ── Right image (2 cols) ── */}\n          <div className=\"lg:col-span-2 relative order-first lg:order-last\">\n            <div className=\"relative mx-auto max-w-[260px] sm:max-w-[290px] lg:max-w-[240px] xl:max-w-[350px]\">\n              {/* Diagonal stripe behind image */}\n              <div\n                className=\"absolute -inset-8 hero-stripe rounded-3xl pointer-events-none\"\n                aria-hidden=\"true\"\n              />\n\n              {/* Outer dashed ring +3° */}\n              <div\n                className=\"absolute inset-0 rounded-2xl border-2 border-dashed border-gray-400 dark:border-gray-600 rotate-3 scale-[1.06] pointer-events-none\"\n                aria-hidden=\"true\"\n              />\n              {/* Inner solid ring -1° */}\n              <div\n                className=\"absolute inset-0 rounded-2xl border border-gray-300 dark:border-gray-700 -rotate-1 scale-[1.02] pointer-events-none\"\n                aria-hidden=\"true\"\n              />\n\n              {/* Profile image */}\n              <div className=\"relative aspect-[3/4] rounded-2xl overflow-hidden border border-gray-200 dark:border-gray-700 shadow-2xl shadow-gray-300 dark:shadow-black/60\">\n                <Image\n                  src={siteConfig.person.profileImage}\n                  className=\"object-cover object-top w-full h-full grayscale hover:grayscale-0 transition-all duration-700\"\n                  fill\n                  alt={siteConfig.person.name}\n                  quality={95}\n                  priority\n                />\n                <div className=\"absolute inset-x-0 bottom-0 h-2/5 bg-gradient-to-t from-black/35 to-transparent pointer-events-none\" />\n              </div>\n\n              {/* Floating card — years */}\n              <motion.div\n                variants={popUp}\n                className=\"absolute -left-5 sm:-left-10 top-10 bg-white dark:bg-darkSecondary border border-gray-200 dark:border-gray-700 border-t-[3px] border-t-neutral-700 dark:border-t-gray-200 rounded-2xl px-4 py-3.5 shadow-[0_12px_40px_-8px_rgba(0,0,0,0.18)] dark:shadow-[0_12px_40px_-8px_rgba(0,0,0,0.6)] flex flex-col items-center min-w-[76px]\"\n              >\n                <span className=\"text-3xl font-black text-gray-900 dark:text-white leading-none\">\n                  {hero.experienceBadge.value}\n                </span>\n                <span className=\"text-[10px] uppercase tracking-[0.14em] text-gray-700 dark:text-gray-300 mt-1.5 font-bold\">\n                  {hero.experienceBadge.title}\n                </span>\n                <span className=\"text-[10px] text-gray-500 dark:text-gray-500\">\n                  {hero.experienceBadge.description}\n                </span>\n              </motion.div>\n\n              {/* Floating card — projects */}\n              <motion.div\n                variants={popUp}\n                className=\"absolute -right-5 sm:-right-10 bottom-16 bg-white dark:bg-darkSecondary border border-gray-200 dark:border-gray-700 border-t-[3px] border-t-neutral-700 dark:border-t-gray-200 rounded-2xl px-4 py-3.5 shadow-[0_12px_40px_-8px_rgba(0,0,0,0.18)] dark:shadow-[0_12px_40px_-8px_rgba(0,0,0,0.6)] flex flex-col items-center min-w-[76px]\"\n              >\n                <span className=\"text-3xl font-black text-gray-900 dark:text-white leading-none\">\n                  100+\n                </span>\n                <span className=\"text-[10px] uppercase tracking-[0.14em] text-gray-700 dark:text-gray-300 mt-1.5 font-bold\">\n                  Articles\n                </span>\n              </motion.div>\n            </div>\n          </div>\n        </div>\n\n        {/* Scroll indicator */}\n        <motion.div\n          variants={opacityVariant}\n          className=\"hidden lg:flex flex-col items-center gap-2 absolute -bottom-4 left-1/2 -translate-x-1/2\"\n        >\n          <span className=\"text-[9px] font-mono uppercase tracking-[0.4em] text-gray-500 dark:text-gray-500\">\n            Scroll\n          </span>\n          <motion.div\n            animate={{ scaleY: [0.4, 1, 0.4], opacity: [0.3, 0.9, 0.3] }}\n            transition={{ duration: 2.5, repeat: Infinity, ease: \"easeInOut\" }}\n            className=\"w-px h-10 bg-gradient-to-b from-gray-500 dark:from-gray-400 to-transparent origin-top\"\n          />\n        </motion.div>\n      </div>\n    </section>\n  );\n}\n"
  },
  {
    "path": "components/Home/SkillSection.tsx",
    "content": "import { motion } from \"framer-motion\";\nimport siteConfig from \"@content/siteConfig\";\nimport skills from \"@content/skillsData\";\nimport React, { useState, useMemo } from \"react\";\n\nconst containerVariants = {\n  hidden: {},\n  visible: { transition: { staggerChildren: 0.04 } },\n};\n\nconst itemVariants = {\n  hidden: { opacity: 0, y: 12, scale: 0.97 },\n  visible: {\n    opacity: 1,\n    y: 0,\n    scale: 1,\n    transition: { type: \"spring\" as const, stiffness: 160, damping: 20 },\n  },\n  exit: { opacity: 0, y: 6, scale: 0.96, transition: { duration: 0.12 } },\n};\n\nexport default function SkillSection() {\n  const [selectedCategory, setSelectedCategory] = useState<string>(\"All\");\n  const { skillsSection } = siteConfig.home;\n\n  const categories = useMemo(() => {\n    const set = new Set(skills.map((s) => s.category || \"Other\"));\n    return [\"All\", ...Array.from(set)];\n  }, []);\n\n  const filteredSkills = useMemo(\n    () =>\n      selectedCategory === \"All\"\n        ? skills\n        : skills.filter((s) => s.category === selectedCategory),\n    [selectedCategory],\n  );\n\n  const categoryCounts = useMemo(() => {\n    const counts: Record<string, number> = { All: skills.length };\n    for (const s of skills) {\n      const cat = s.category || \"Other\";\n      counts[cat] = (counts[cat] ?? 0) + 1;\n    }\n    return counts;\n  }, []);\n\n  return (\n    <section className=\"pt-20 sm:pt-24 relative overflow-hidden\">\n      {/* Section number watermark */}\n      <div\n        className=\"absolute -right-2 top-6 font-black select-none pointer-events-none leading-none tracking-tighter bg-gradient-to-b from-gray-200 to-gray-50 dark:from-[#232628] dark:to-darkPrimary bg-clip-text text-transparent\"\n        style={{ fontSize: \"clamp(5rem, 16vw, 13rem)\" }}\n        aria-hidden=\"true\"\n      >\n        02\n      </div>\n\n      {/* ── Header ── */}\n      <div className=\"relative z-10 flex flex-col lg:flex-row lg:items-end lg:justify-between gap-8 mb-12\">\n        <div className=\"space-y-3 max-w-xl\">\n          {/* Section label */}\n          <motion.div\n            initial={{ opacity: 0, x: -16 }}\n            whileInView={{ opacity: 1, x: 0 }}\n            viewport={{ once: true }}\n            className=\"flex items-center gap-3\"\n          >\n            <div className=\"h-px w-5 bg-gray-400 dark:bg-gray-600 flex-shrink-0\" />\n            <span className=\"font-mono text-[10px] tracking-[0.45em] uppercase text-gray-400 dark:text-gray-500\">\n              {skillsSection.eyebrow}\n            </span>\n          </motion.div>\n\n          <motion.h2\n            initial={{ opacity: 0, y: 20 }}\n            whileInView={{ opacity: 1, y: 0 }}\n            viewport={{ once: true }}\n            transition={{ delay: 0.1 }}\n            className=\"text-3xl sm:text-4xl lg:text-5xl font-bold text-gray-900 dark:text-white\"\n          >\n            {skillsSection.title}\n          </motion.h2>\n\n          <motion.p\n            initial={{ opacity: 0, y: 16 }}\n            whileInView={{ opacity: 1, y: 0 }}\n            viewport={{ once: true }}\n            transition={{ delay: 0.15 }}\n            className=\"text-base text-gray-600 dark:text-gray-400 border-l-2 border-gray-300 dark:border-gray-700 pl-4 py-0.5\"\n          >\n            {skillsSection.description}\n          </motion.p>\n        </div>\n\n        {/* Tech count + category breakdown */}\n        <motion.div\n          initial={{ opacity: 0, x: 20 }}\n          whileInView={{ opacity: 1, x: 0 }}\n          viewport={{ once: true }}\n          transition={{ delay: 0.15 }}\n          className=\"flex flex-col items-start lg:items-end gap-3 flex-shrink-0\"\n        >\n          <div className=\"flex items-end gap-2\">\n            <span className=\"text-5xl sm:text-6xl font-black text-gray-900 dark:text-white leading-none\">\n              {skills.length}\n            </span>\n            <span className=\"text-xs font-mono uppercase tracking-widest text-gray-500 dark:text-gray-500 mb-2\">\n              Technologies\n            </span>\n          </div>\n          <div className=\"flex flex-wrap gap-x-4 gap-y-1.5 max-w-xs lg:justify-end\">\n            {categories.slice(1).map((cat) => (\n              <button\n                key={cat}\n                onClick={() => setSelectedCategory(cat)}\n                className=\"inline-flex items-center gap-1.5 text-[10px] font-mono uppercase tracking-[0.2em] text-gray-500 dark:text-gray-500 hover:text-gray-900 dark:hover:text-gray-200 transition-colors\"\n              >\n                <span\n                  className={`w-1 h-1 rounded-full transition-colors ${\n                    selectedCategory === cat\n                      ? \"bg-gray-900 dark:bg-white\"\n                      : \"bg-gray-400 dark:bg-gray-600\"\n                  }`}\n                />\n                {cat}\n                <span className=\"opacity-60\">({categoryCounts[cat]})</span>\n              </button>\n            ))}\n          </div>\n        </motion.div>\n      </div>\n\n      {/* ── Marquee divider ── */}\n      <motion.div\n        initial={{ opacity: 0 }}\n        whileInView={{ opacity: 1 }}\n        viewport={{ once: true }}\n        className=\"relative my-10 overflow-hidden border-y border-gray-200 dark:border-neutral-700 py-3.5\"\n      >\n        <div className=\"flex animate-marquee gap-10 w-max\">\n          {[...skills, ...skills].map((skill, i) => {\n            const Icon = skill.Icon;\n            return (\n              <div\n                key={i}\n                className=\"inline-flex items-center gap-2 text-gray-600 dark:text-gray-400 select-none\"\n              >\n                {/* @ts-ignore */}\n                <Icon className=\"w-3.5 h-3.5 flex-shrink-0\" />\n                <span className=\"text-[10px] font-mono uppercase tracking-[0.25em] whitespace-nowrap\">\n                  {skill.name}\n                </span>\n              </div>\n            );\n          })}\n        </div>\n        {/* Side fades */}\n        <div className=\"absolute inset-y-0 left-0 w-16 bg-gradient-to-r from-darkWhite dark:from-darkPrimary to-transparent pointer-events-none\" />\n        <div className=\"absolute inset-y-0 right-0 w-16 bg-gradient-to-l from-darkWhite dark:from-darkPrimary to-transparent pointer-events-none\" />\n      </motion.div>\n\n      {/* ── Category filter ── */}\n      <motion.div\n        initial={{ opacity: 0, y: -8 }}\n        whileInView={{ opacity: 1, y: 0 }}\n        viewport={{ once: true }}\n        className=\"flex flex-wrap gap-2 mb-8\"\n      >\n        {categories.map((cat) => (\n          <button\n            key={cat}\n            onClick={() => setSelectedCategory(cat)}\n            className={`px-4 py-2 text-xs font-semibold font-mono uppercase tracking-wide transition-all duration-200 ${\n              selectedCategory === cat\n                ? \"bg-gray-900 dark:bg-white text-white dark:text-gray-900\"\n                : \"bg-transparent text-gray-600 dark:text-gray-400 border border-gray-200 dark:border-gray-700 hover:border-gray-500 dark:hover:border-gray-500 hover:text-gray-900 dark:hover:text-white\"\n            }`}\n          >\n            {cat}\n            <span className=\"ml-1.5 opacity-50 text-[9px]\">\n              {categoryCounts[cat]}\n            </span>\n          </button>\n        ))}\n      </motion.div>\n\n      {/* ── Skills grid — keyed so the whole grid stagger-enters on every filter change ── */}\n      <motion.div\n        key={selectedCategory}\n        variants={containerVariants}\n        initial=\"hidden\"\n        animate=\"visible\"\n        className=\"grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-3\"\n      >\n        {filteredSkills.map((skill) => {\n          const Icon = skill.Icon;\n          return (\n            <motion.div\n              key={skill.name}\n              variants={itemVariants}\n              whileHover={{ y: -4 }}\n              className=\"group relative flex items-center gap-3 px-4 py-3.5 bg-white dark:bg-darkSecondary border border-gray-200 dark:border-gray-700 hover:border-gray-400 dark:hover:border-gray-600 transition-all duration-200 overflow-hidden cursor-default\"\n            >\n              {/* Hover background */}\n              <div className=\"absolute inset-0 bg-gray-50 dark:bg-darkPrimary opacity-0 group-hover:opacity-100 transition-opacity duration-200\" />\n\n              {/* Left accent bar */}\n              <div className=\"absolute left-0 inset-y-0 w-0.5 bg-gray-900 dark:bg-white origin-center scale-y-0 group-hover:scale-y-100 transition-transform duration-250 rounded-sm\" />\n\n              {/* Icon */}\n              <div className=\"relative flex-shrink-0 w-8 h-8 bg-gray-100 dark:bg-white/10 group-hover:bg-gray-900 dark:group-hover:bg-white flex items-center justify-center transition-colors duration-200\">\n                {/* @ts-ignore */}\n                <Icon className=\"w-4 h-4 text-gray-700 dark:text-gray-300 group-hover:text-white dark:group-hover:text-gray-900 transition-colors duration-200\" />\n              </div>\n\n              {/* Name */}\n              <span className=\"relative text-sm font-semibold text-gray-700 dark:text-gray-300 group-hover:text-gray-900 dark:group-hover:text-white transition-colors duration-200 truncate leading-tight\">\n                {skill.name}\n              </span>\n            </motion.div>\n          );\n        })}\n      </motion.div>\n    </section>\n  );\n}\n"
  },
  {
    "path": "components/Home/StatsSection.tsx",
    "content": "import { motion } from \"framer-motion\";\nimport { FiCode, FiUsers, FiCoffee, FiAward } from \"react-icons/fi\";\nimport siteConfig, { StatIconKey } from \"@content/siteConfig\";\n\nconst iconMap: Record<StatIconKey, typeof FiCode> = {\n  code: FiCode,\n  users: FiUsers,\n  coffee: FiCoffee,\n  award: FiAward,\n};\n\nconst containerVariants = {\n  hidden: { opacity: 0 },\n  visible: {\n    opacity: 1,\n    transition: {\n      staggerChildren: 0.1,\n    },\n  },\n};\n\nconst itemVariants = {\n  hidden: { opacity: 0, y: 20 },\n  visible: {\n    opacity: 1,\n    y: 0,\n    transition: {\n      duration: 0.5,\n      ease: [0.22, 1, 0.36, 1],\n    },\n  },\n};\n\nexport default function StatsSection() {\n  return (\n    <section className=\"py-20 sm:py-24\">\n      <motion.div\n        variants={containerVariants}\n        initial=\"hidden\"\n        whileInView=\"visible\"\n        viewport={{ once: true }}\n        className=\"grid grid-cols-2 lg:grid-cols-4 gap-6 sm:gap-8\"\n      >\n        {siteConfig.home.stats.map((stat) => {\n          const Icon = iconMap[stat.icon];\n\n          return (\n            <motion.div\n              key={stat.label}\n              variants={itemVariants}\n              whileHover={{ y: -8 }}\n              className=\"relative p-6 sm:p-8 rounded-2xl bg-white dark:bg-darkSecondary border-2 border-gray-100 dark:border-neutral-700 hover:border-gray-300 dark:hover:border-gray-700 transition-all duration-300 group overflow-hidden\"\n            >\n              {/* Background Gradient */}\n              <div className=\"absolute inset-0 bg-gradient-to-br from-gray-50 to-gray-100 dark:from-neutral-700 dark:to-gray-900 opacity-0 group-hover:opacity-100 transition-opacity duration-300\" />\n\n              {/* Content */}\n              <div className=\"relative z-10 text-center space-y-3\">\n                {/* Icon */}\n                <div className=\"inline-flex p-3 rounded-xl bg-gray-100 dark:bg-gray-900 group-hover:bg-gray-900 dark:group-hover:bg-white transition-all duration-300\">\n                  <Icon className=\"w-6 h-6 text-gray-700 dark:text-gray-300 group-hover:text-white dark:group-hover:text-gray-900 transition-colors duration-300\" />\n                </div>\n\n                {/* Value */}\n                <div className=\"text-3xl sm:text-4xl font-bold text-gray-900 dark:text-white\">\n                  {stat.value}\n                </div>\n\n                {/* Label */}\n                <div className=\"space-y-1\">\n                  <div className=\"text-sm font-semibold text-gray-700 dark:text-gray-300\">\n                    {stat.label}\n                  </div>\n                  <div className=\"text-xs text-gray-500 dark:text-gray-500\">\n                    {stat.description}\n                  </div>\n                </div>\n              </div>\n            </motion.div>\n          );\n        })}\n      </motion.div>\n    </section>\n  );\n}\n"
  },
  {
    "path": "components/Instagram/InstagramPost.tsx",
    "content": "import { DetailedInstagramPost, MediaType } from \"@lib/interface\";\n\nimport { FaPlay } from \"react-icons/fa\";\nimport Link from \"next/link\";\nimport React from \"react\";\nimport { TbBoxMultiple } from \"react-icons/tb\";\n\nexport default function InstagramPost({\n  post,\n}: {\n  post: DetailedInstagramPost;\n}) {\n  const previewURL =\n    post.media_type === MediaType.Video ? post.thumbnail_url! : post.media_url;\n  return (\n    <Link\n      href={post.permalink}\n      target=\"_blank\"\n      rel=\"noopener noreferrer\"\n      className=\"w-full aspect-square relative group\"\n    >\n      <div className=\"absolute hidden inset-0 bg-gradient-to-t from-black to-black/30 p-5 lg:group-hover:flex \">\n        <div className=\"text-white text-sm line-clamp-2 mt-auto\">\n          {post.caption}\n        </div>\n      </div>\n\n      {post.media_type !== MediaType.Image && (\n        <div\n          title={post.media_type.replace(\"_\", \" \").toLowerCase()}\n          className=\"absolute right-1 top-1 sm:right-2 sm:top-2 hover:bg-black/50 p-1 sm:p-2 rounded-full\"\n        >\n          {post.media_type === MediaType.Video && (\n            <FaPlay className=\"text-white drop-shadow-md\" />\n          )}\n          {post.media_type === MediaType.CarouselAlbum && (\n            <TbBoxMultiple className=\"text-white drop-shadow-md\" />\n          )}\n        </div>\n      )}\n\n      <img\n        src={previewURL}\n        width={300}\n        height={300}\n        alt={post.caption ?? \"\"}\n        className=\"object-cover h-full w-full\"\n      />\n    </Link>\n  );\n}\n"
  },
  {
    "path": "components/Instagram/InstagramPostLoading.tsx",
    "content": "import React from \"react\";\n\nexport default function InstagramPostLoading({ count }: { count: number }) {\n  return (\n    <>\n      {Array.from(Array(count).keys()).map((item) => {\n        return (\n          <div\n            key={item}\n            style={{\n              animationDelay: `calc(${item} * 200ms)`,\n            }}\n            className=\"aspect-square bg-neutral-300 dark:bg-neutral-700 animate-pulse \"\n          />\n        );\n      })}\n    </>\n  );\n}\n"
  },
  {
    "path": "components/Instagram/InstagramSection.tsx",
    "content": "import { HomeHeading } from \"pages\";\nimport { InstagramData } from \"@lib/interface\";\nimport InstagramPost from \"./InstagramPost\";\nimport InstagramPostLoading from \"./InstagramPostLoading\";\nimport Link from \"next/link\";\nimport React from \"react\";\nimport fetcher from \"@lib/fetcher\";\nimport socialMedia from \"@content/socialMedia\";\nimport useSWR from \"swr\";\n\nexport default function InstagramSection() {\n  const { data: instaData } = useSWR<InstagramData>(\n    \"/api/posts/insta\",\n    fetcher\n  );\n  return (\n    <section className=\"mx-5 mb-5\">\n      <HomeHeading title=\"Recent Instagram Posts\" />\n\n      <div className=\"grid grid-cols-1 gap-4 mx-auto mt-5\">\n        <div className=\"grid grid-cols-3 gap-0.5\">\n          {instaData === undefined ? (\n            <InstagramPostLoading count={9} />\n          ) : (\n            instaData?.data.slice(0, 9).map((post) => {\n              return <InstagramPost key={post.id} post={post} />;\n            })\n          )}\n        </div>\n\n        <Link\n          href={socialMedia.find((item) => item.title === \"Instagram\")?.url!}\n          className=\"flex items-center justify-center gap-1 font-medium transition border-transparent font-inter active:scale-95 active:border-black w-fit group\"\n        >\n          <span className=\"group-hover:underline\">View More on Instagram</span>\n          <svg\n            xmlns=\"http://www.w3.org/2000/svg\"\n            fill=\"none\"\n            viewBox=\"0 0 24 24\"\n            className=\"w-6 h-6 ml-1 transition group-hover:translate-x-2\"\n          >\n            <path\n              stroke=\"currentColor\"\n              strokeLinecap=\"round\"\n              strokeLinejoin=\"round\"\n              strokeWidth=\"2\"\n              d=\"M17.5 12h-15m11.667-4l3.333 4-3.333-4zm3.333 4l-3.333 4 3.333-4z\"\n            ></path>\n          </svg>\n        </Link>\n      </div>\n    </section>\n  );\n}\n"
  },
  {
    "path": "components/MDXComponents/Code.tsx",
    "content": "type Props = {\n  children?: string | React.ReactNode;\n};\n\nexport default function Code(props: Props) {\n  return (\n    <>\n      {typeof props.children === \"string\" ? (\n        <code className=\"bg-gray-300 dark:bg-darkSecondary text-black dark:text-white p-0.5 rounded before:text-gray-500 after:text-gray-500 \">\n          {props.children}\n        </code>\n      ) : (\n        <code>{props.children}</code>\n      )}\n    </>\n  );\n}\n"
  },
  {
    "path": "components/MDXComponents/CodeSandbox.tsx",
    "content": "export default function CodeSandbox({\n  id,\n  hideNavigation = true,\n}: {\n  id: string;\n  hideNavigation: boolean;\n}) {\n  return (\n    <div className=\"my-3 print:hidden\">\n      <h3>Code Sandbox</h3>\n      <iframe\n        className=\"w-full h-[500px] border-0 rounded overflow-hidden\"\n        src={`https://codesandbox.io/embed/${id}?fontsize=14&theme=dark&hidenavigation=${\n          hideNavigation ? 1 : 0\n        }`}\n        allow=\"accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking\"\n        sandbox=\"allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts\"\n      ></iframe>\n    </div>\n  );\n}\n"
  },
  {
    "path": "components/MDXComponents/CodeTitle.tsx",
    "content": "import { BsFileEarmarkCodeFill } from \"react-icons/bs\";\nimport {\n  SiCss3,\n  SiPython,\n  SiGnubash,\n  SiHtml5,\n  SiReact,\n  SiMarkdown,\n  SiNextdotjs,\n  SiVercel,\n  SiTypescript,\n} from \"react-icons/si\";\nimport { VscJson } from \"react-icons/vsc\";\nimport { IoLogoJavascript } from \"react-icons/io5\";\nimport { AiOutlineFileText, AiOutlineFolderOpen } from \"react-icons/ai\";\n\ntype Props = {\n  title?: string;\n  lang: string;\n};\n\nexport default function CodeTitle({ title, lang }: Props) {\n  let Icon;\n  switch (lang) {\n    case \"html\":\n      Icon = SiHtml5;\n      break;\n    case \"css\":\n      Icon = SiCss3;\n      break;\n    case \"js\":\n      Icon = IoLogoJavascript;\n      break;\n    case \"bash\":\n      Icon = SiGnubash;\n      break;\n    case \"py\":\n      Icon = SiPython;\n      break;\n    case \"json\":\n      Icon = VscJson;\n      break;\n    case \"jsx\":\n      Icon = SiReact;\n      break;\n    case \"text\":\n      Icon = AiOutlineFileText;\n      break;\n    case \"md\":\n      Icon = SiMarkdown;\n      break;\n    case \"next\":\n      Icon = SiNextdotjs;\n      break;\n    case \"directory\":\n      Icon = AiOutlineFolderOpen;\n      break;\n    case \"vercel\":\n      Icon = SiVercel;\n      break;\n    case \"ts\":\n    case \"tsx\":\n      Icon = SiTypescript;\n      break;\n    default:\n      Icon = BsFileEarmarkCodeFill;\n      break;\n  }\n\n  return (\n    <div className=\"!mt-4 mb-[14px]\">\n      <div className=\"h-0.5 w-full bg-gray-900 dark:bg-white\" />\n      <div className=\"bg-white dark:bg-darkSecondary border border-b-0 border-gray-200 dark:border-neutral-700 px-4 py-2 flex items-center gap-2 font-mono overflow-x-auto\">\n        <Icon className=\"w-3.5 h-3.5 text-gray-400 dark:text-gray-500 flex-shrink-0\" />\n        <span className=\"text-[10px] tracking-[0.35em] uppercase text-gray-600 dark:text-gray-400 whitespace-nowrap\">\n          {title || lang}\n        </span>\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "components/MDXComponents/Codepen.tsx",
    "content": "export default function Codepen({ id }: { id: string }) {\n  return (\n    <div className=\"my-3 print:hidden\">\n      <iframe\n        height=\"600\"\n        style={{ marginTop: \"10px\" }}\n        className=\"w-full\"\n        src={`https://codepen.io/j471n/embed/${id}`}\n        loading=\"lazy\"\n        allowFullScreen={true}\n      ></iframe>\n    </div>\n  );\n}\n"
  },
  {
    "path": "components/MDXComponents/Danger.tsx",
    "content": "type Props = { title?: string; text: string };\n\nexport default function Danger({ title, text }: Props) {\n  return (\n    <div className=\"w-full p-6 my-4 bg-red-100 border-l-4 border-red-700 dark:border-red-500 dark:bg-red-700/30\">\n      <div className=\"flex items-center gap-2 mb-2 text-2xl font-medium leading-tight text-red-700 dark:text-red-500\">\n        <svg\n          aria-hidden=\"true\"\n          focusable=\"false\"\n          data-icon=\"times-circle\"\n          className=\"w-4 h-4 mr-2 fill-current\"\n          role=\"img\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          viewBox=\"0 0 512 512\"\n        >\n          <path\n            fill=\"currentColor\"\n            d=\"M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm121.6 313.1c4.7 4.7 4.7 12.3 0 17L338 377.6c-4.7 4.7-12.3 4.7-17 0L256 312l-65.1 65.6c-4.7 4.7-12.3 4.7-17 0L134.4 338c-4.7-4.7-4.7-12.3 0-17l65.6-65-65.6-65.1c-4.7-4.7-4.7-12.3 0-17l39.6-39.6c4.7-4.7 12.3-4.7 17 0l65 65.7 65.1-65.6c4.7-4.7 12.3-4.7 17 0l39.6 39.6c4.7 4.7 4.7 12.3 0 17L312 256l65.6 65.1z\"\n          ></path>\n        </svg>\n        {title || \"Danger\"}\n      </div>\n      <p className=\"mt-4 text-red-700/80 dark:text-red-400/50\">{text}</p>\n    </div>\n  );\n}\n"
  },
  {
    "path": "components/MDXComponents/EmbedBlog.tsx",
    "content": "import Image from \"next/image\";\nimport Link from \"next/link\";\n\nexport default function EmbedBlog({\n  img,\n  text,\n  url,\n}: {\n  img: string;\n  text: string;\n  url: string;\n}) {\n  return (\n    <Link\n      href={url}\n      className=\"flex items-center gap-3 my-5 px-2 py-2 sm:py-0 border-black dark:border-white border-2 shadow-[5px_5px_black] dark:rounded-md dark:shadow-none select-none\"\n      target=\"_blank\"\n      rel=\"noopener noreferrer\"\n    >\n      <div className=\"flex\">\n        <Image\n          src={img}\n          width={100}\n          height={55}\n          placeholder=\"blur\"\n          blurDataURL={img}\n          alt=\"blog image\"\n          style={{\n            maxWidth: \"100%\",\n            height: \"auto\",\n          }}\n        />\n      </div>\n      <p className=\"font-bold sm:text-lg\">{text}</p>\n    </Link>\n  );\n}\n"
  },
  {
    "path": "components/MDXComponents/Figcaption.tsx",
    "content": "type Props = {\n  src: string;\n  caption?: string;\n  alt?: string;\n};\n\nexport default function figcaption({ src, caption, alt }: Props) {\n  if (caption !== undefined) {\n    return (\n      <figure>\n        <img src={src} alt={alt} />\n        <figcaption>{caption}</figcaption>\n      </figure>\n    );\n  } else {\n    return <img src={src} alt={alt} />;\n  }\n}\n"
  },
  {
    "path": "components/MDXComponents/LinkedInEmbed.tsx",
    "content": "export default function LinkedInEmbed({ id }: { id: string }) {\n  return (\n    <div\n      style={{\n        position: \"relative\",\n        overflow: \"hidden\",\n        paddingTop: 56.25 + \"%\",\n      }}\n    >\n      <iframe\n        style={{\n          position: \"absolute\",\n          top: 0,\n          left: 0,\n          width: 100 + \"%\",\n          height: 100 + \"%\",\n          border: 0,\n        }}\n        src={`https://www.linkedin.com/embed/feed/update/urn:li:activity:${id}`}\n      />\n    </div>\n  );\n}\n"
  },
  {
    "path": "components/MDXComponents/NextAndPreviousButton.tsx",
    "content": "import Link from \"next/link\";\nimport { IoArrowForwardSharp } from \"react-icons/io5\";\n\ntype Props = {\n  prevHref: string;\n  prevTitle: string;\n  nextHref: string;\n  nextTitle: string;\n};\n\nexport default function NextAndPreviousButton({\n  prevHref,\n  prevTitle,\n  nextHref,\n  nextTitle,\n}: Props) {\n  return (\n    <div className=\"flex flex-col gap-2 lg:flex-row \">\n      {prevHref && prevTitle && (\n        <BlogPageButton href={prevHref} title={prevTitle} type=\"previous\" />\n      )}\n      {nextHref && nextTitle && (\n        <BlogPageButton href={nextHref} title={nextTitle} type=\"next\" />\n      )}\n    </div>\n  );\n}\n\nfunction BlogPageButton({\n  href,\n  title,\n  type,\n}: {\n  href: string;\n  title: string;\n  type: \"previous\" | \"next\";\n}) {\n  return (\n    <Link\n      title={title}\n      href={href}\n      className={`flex ${\n        type === \"previous\" && \"flex-row-reverse\"\n      } justify-between bg-neutral-800 hover:bg-black !no-underline p-3 rounded-md active:scale-95 transition w-full shadow dark:hover:ring-1 dark:ring-white`}\n    >\n      <div\n        className={`flex flex-col gap-1 ${type === \"previous\" && \"text-right\"}`}\n      >\n        <p className=\"text-gray-300  !my-0 capitalize text-sm sm:font-light\">\n          {type} Article\n        </p>\n        <p className=\"text-white font-bold sm:font-medium !my-0 text-base\">\n          {title}\n        </p>\n      </div>\n      <IoArrowForwardSharp\n        className={`bg-white text-black p-2 rounded-full w-8 h-8 self-center ${\n          type === \"previous\" && \"rotate-180\"\n        }`}\n      />\n    </Link>\n  );\n}\n"
  },
  {
    "path": "components/MDXComponents/Pre.tsx",
    "content": "import { ReactNode, useRef, useState } from \"react\";\nimport { MdCheck, MdContentCopy } from \"react-icons/md\";\n\nconst Pre = ({\n  children,\n  \"data-theme\": dataTheme,\n}: {\n  children?: ReactNode;\n  \"data-theme\"?: string;\n}) => {\n  const textInput = useRef<HTMLDivElement>(null);\n  const [copied, setCopied] = useState(false);\n\n  const onCopy = () => {\n    if (textInput.current !== null) {\n      setCopied(true);\n      navigator.clipboard.writeText(textInput.current.textContent!);\n      setTimeout(() => setCopied(false), 2000);\n    }\n  };\n\n  return (\n    <div\n      className=\"relative mb-3 -mt-[14px]\"\n      data-theme={dataTheme}\n      ref={textInput}\n    >\n      <button\n        aria-label=\"Copy code\"\n        type=\"button\"\n        onClick={onCopy}\n        className={`absolute top-2 right-2 z-10 flex items-center gap-1.5 px-2.5 py-1.5 border font-mono text-[10px] tracking-[0.3em] uppercase transition-colors ${\n          copied\n            ? \"bg-gray-900 dark:bg-white text-white dark:text-gray-900 border-gray-900 dark:border-white\"\n            : \"bg-transparent border-gray-400 dark:border-gray-600 text-gray-400 dark:text-gray-500 hover:bg-gray-900 dark:hover:bg-white hover:text-white dark:hover:text-gray-900 hover:border-gray-900 dark:hover:border-white\"\n        }`}\n      >\n        {copied ? (\n          <MdCheck className=\"w-3 h-3 flex-shrink-0\" />\n        ) : (\n          <MdContentCopy className=\"w-3 h-3 flex-shrink-0\" />\n        )}\n        <span className=\"hidden sm:inline\">{copied ? \"Copied\" : \"Copy\"}</span>\n      </button>\n\n      <pre className=\"blog-pre !my-0 !w-full !p-0 !py-3 border border-gray-200 dark:border-neutral-700\">\n        {children}\n      </pre>\n    </div>\n  );\n};\n\nexport default Pre;\n"
  },
  {
    "path": "components/MDXComponents/Step.tsx",
    "content": "import React from \"react\";\n\nexport default function Step({\n  id,\n  children,\n}: {\n  id: string;\n  children?: JSX.Element;\n}) {\n  return (\n    <div\n      className={`flex items-center gap-3 ${\n        children?.type === undefined && \"my-5\"\n      }`}\n    >\n      <div className=\"flex items-center justify-center w-10 h-10 p-5 font-bold text-black bg-gray-300 rounded-full dark:border-neutral-700 g-gray-300 ring dark:bg-darkSecondary dark:text-white \">\n        {id}\n      </div>\n      <div className=\"flex-grow-0 text-lg font-semibold tracking-tight text-black dark:text-white w-fit\">\n        {children}\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "components/MDXComponents/Tip.tsx",
    "content": "export default function Tip({\n  id,\n  children,\n}: {\n  id: string;\n  children?: React.ReactNode;\n}) {\n  return (\n    <div className=\"relative flex items-center w-full gap-2 px-5 pt-4 my-5 bg-yellow-100 rounded-md dark:bg-neutral-800\">\n      <div className=\"absolute top-0 left-0 px-4 py-0 font-bold text-black uppercase bg-yellow-400 font-barlow rounded-tl-md rounded-br-md dark:bg-yellow-500\">\n        Tip {id && `#${id}`}\n      </div>\n      {children}\n    </div>\n  );\n}\n"
  },
  {
    "path": "components/MDXComponents/UrlMetaInfo.tsx",
    "content": "import React, { useEffect, useState } from \"react\";\n\nimport Image from \"next/image\";\nimport Link from \"next/link\";\n\ninterface MetaData {\n  title: string;\n  description: string;\n  image: string;\n}\n\ninterface UrlMetaInfoProps {\n  url: string;\n}\n\nfunction UrlMetaInfo({ url }: UrlMetaInfoProps) {\n  const [metaData, setMetaData] = useState<MetaData | null>(null);\n\n  useEffect(() => {\n    const fetchData = async () => {\n      try {\n        const response = await fetch(url);\n        const data = await response.text();\n        const parser = new DOMParser();\n        const htmlDoc = parser.parseFromString(data, \"text/html\");\n        const metaTags = htmlDoc.querySelectorAll(\"meta\");\n\n        const metaInfo: MetaData = {\n          title: (htmlDoc.querySelector(\"title\")?.textContent || \"\")\n            .replaceAll(\" - Jatin Sharma\", \"\")\n            .replaceAll(\" - DEV Community\", \"\"),\n          description: (\n            Array.from(metaTags)\n              .find((tag) => tag.getAttribute(\"name\") === \"description\")\n              ?.getAttribute(\"content\") || \"\"\n          ).split(\"...\")[0],\n          image:\n            Array.from(metaTags)\n              .find((tag) => tag.getAttribute(\"property\") === \"og:image\")\n              ?.getAttribute(\"content\") || \"\",\n        };\n\n        setMetaData(metaInfo);\n      } catch (error) {\n        console.error(\"Error fetching URL data:\", error);\n      }\n    };\n\n    fetchData();\n  }, [url]);\n\n  if (!metaData) {\n    return <div>Loading...</div>;\n  }\n\n  return (\n    <Link\n      target=\"_blank\"\n      rel=\"noopener noreferrer\"\n      href={url}\n      passHref\n      className=\"!no-underline\"\n    >\n      <div className=\"flex gap-2 ring-2 rounded-lg ring-gray-500 relative items-center dark:hover:bg-darkSecondary cursor-pointer unset my-4\">\n        <div className=\"flex flex-col gap-2 !m-0 px-5 py-5 sm:py-0\">\n          <h4 className=\"!m-0 line-clamp-1\">{metaData.title}</h4>\n          <p className=\"line-clamp-2 !m-0 text-sm !text-gray-400\">\n            {metaData.description}\n          </p>\n        </div>\n        <div className=\"w-[184px] shrink-0 hidden sm:flex\">\n          <Image\n            width={300}\n            height={200}\n            className=\"h-full w-full !m-0 rounded-lg object-contain\"\n            src={metaData.image}\n            alt={metaData.title}\n          />\n        </div>\n      </div>\n    </Link>\n  );\n}\n\nexport default UrlMetaInfo;\n"
  },
  {
    "path": "components/MDXComponents/Warning.tsx",
    "content": "type Props = {\n  text?: string;\n  title?: string;\n  children?: React.ReactNode;\n};\n\nexport default function Warning({ text, title, children }: Props) {\n  return (\n    <div className=\"w-full p-6 my-4 bg-yellow-100 border-l-4 border-yellow-700 dark:border-yellow-500 dark:bg-yellow-900\">\n      <div className=\"flex items-center gap-2 mb-2 text-2xl font-medium leading-tight text-yellow-700 dark:text-yellow-500\">\n        <svg\n          aria-hidden=\"true\"\n          className=\"w-6 h-6 mr-2 fill-current\"\n          role=\"img\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n          viewBox=\"0 0 576 512\"\n        >\n          <path\n            fill=\"currentColor\"\n            d=\"M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z\"\n          ></path>\n        </svg>\n        {title || \"Warning\"}\n      </div>\n      <p className=\"mt-4 text-yellow-700/80 dark:text-yellow-400/50\">\n        {text || children}\n      </p>\n    </div>\n  );\n}\n"
  },
  {
    "path": "components/MDXComponents/YouTube.tsx",
    "content": "export default function YouTube({ id }: { id: string }) {\n  return (\n    <div className=\"max-w-full overflow-hidden relative pb-[56.25%] h-0 \">\n      <iframe\n        className=\"absolute top-0 left-0 w-full h-full\"\n        src={`https://www.youtube.com/embed/${id}`}\n        title=\"YouTube video player\"\n        allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n        allowFullScreen\n      ></iframe>\n    </div>\n  );\n}\n"
  },
  {
    "path": "components/MDXComponents/index.tsx",
    "content": "import Code from \"./Code\";\nimport CodeSandbox from \"./CodeSandbox\";\nimport CodeTitle from \"./CodeTitle\";\nimport Codepen from \"./Codepen\";\nimport Danger from \"./Danger\";\nimport EmbedBlog from \"./EmbedBlog\";\nimport Figcaption from \"./Figcaption\";\nimport LinkedInEmbed from \"./LinkedInEmbed\";\nimport NextAndPreviousButton from \"./NextAndPreviousButton\";\nimport Pre from \"./Pre\";\nimport Step from \"./Step\";\nimport Tip from \"./Tip\";\nimport UrlMetaInfo from \"./UrlMetaInfo\";\nimport Warning from \"./Warning\";\nimport YouTube from \"./YouTube\";\n\nconst MDXComponents = {\n  Codepen,\n  Figcaption,\n  Warning,\n  Danger,\n  CodeTitle,\n  Tip,\n  Step,\n  CodeSandbox,\n  NextAndPreviousButton,\n  YouTube,\n  EmbedBlog,\n  UrlMetaInfo,\n  LinkedInEmbed,\n  pre: Pre,\n  code: Code,\n};\n\nexport default MDXComponents;\n"
  },
  {
    "path": "components/MetaData.tsx",
    "content": "import { useEffect, useState } from \"react\";\n\nimport { NextSeo } from \"next-seo\";\nimport useWindowLocation from \"@hooks/useWindowLocation\";\n\n// import Head from \"next/head\";\n\ntype Props = {\n  title: string;\n  description: string;\n  previewImage?: string;\n  keywords?: string;\n  suffix?: string;\n};\n\nconst getFaviconPath = (isDarkMode: boolean = true): string => {\n  return `/favicon-${isDarkMode ? \"dark\" : \"light\"}.ico`;\n};\n\nexport default function MetaData({\n  title,\n  description,\n  previewImage,\n  keywords,\n  suffix,\n}: Props) {\n  const { currentURL } = useWindowLocation();\n  const [faviconHref, setFaviconHref] = useState(\"/favicon-dark.ico\");\n\n  useEffect(() => {\n    // Get current color scheme.\n    const matcher = window.matchMedia(\"(prefers-color-scheme: dark)\");\n    // Set favicon initially.\n    setFaviconHref(getFaviconPath(matcher.matches));\n    // Change favicon if the color scheme changes.\n    matcher.onchange = () => setFaviconHref(getFaviconPath(matcher.matches));\n  }, [faviconHref]);\n\n  return (\n    <NextSeo\n      title={title + (suffix ? ` - ${suffix}` : \"\")}\n      description={description || \"Jatin Sharma\"}\n      canonical={currentURL}\n      additionalLinkTags={[\n        {\n          rel: \"icon\",\n          href: faviconHref,\n        },\n        {\n          rel: \"manifest\",\n          href: \"/manifest.json\",\n        },\n        {\n          rel: \"apple-touch-icon\",\n          href: \"/icons/icon-192x192.png\",\n          sizes: \"192x192\",\n        },\n      ]}\n      openGraph={{\n        type: \"website\",\n        url: currentURL,\n        title: title + (suffix ? ` - ${suffix}` : \"\"),\n        description: description || \"Jatin Sharma\",\n        profile: {\n          firstName: \"Jatin\",\n          lastName: \"Sharma\",\n          gender: \"Male\",\n          username: \"j471n\",\n        },\n        article: {\n          tags: keywords?.split(\",\"),\n          authors: [\"https://linkedin.com/in/j471n\"],\n        },\n        images: [\n          {\n            url: previewImage ?? \"\",\n            width: 1200,\n            height: 630,\n            alt: title,\n            type: \"image/jpeg\",\n          },\n        ],\n        siteName: \"j471n.in\",\n      }}\n      twitter={{\n        handle: \"@j471n_\",\n        site: \"@j471n_\",\n        cardType: \"summary_large_image\",\n      }}\n    />\n    // <Head>\n    //   <meta charSet=\"utf-8\" />\n    //   <meta\n    //     name=\"viewport\"\n    //     content=\"width=device-width,initial-scale=1,minimum-scale=1\"\n    //   />\n    //   <meta httpEquiv=\"X-UA-Compatible\" content=\"IE=edge\" />\n    //   <meta name=\"description\" content={description || \"Jatin Sharma\"} />\n    //   <title>{title + (suffix ? ` - ${suffix}` : \"\")}</title>\n    //   <meta name=\"theme-color\" content=\"#000\" />\n    //   <link rel=\"shortcut icon\" href={faviconHref} sizes=\"any\" />\n    //   <link rel=\"manifest\" href=\"/manifest.json\" />\n    //   <link rel=\"apple-touch-icon\" href=\"/icons/icon-192x192.png\"></link>\n    //   <meta httpEquiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n    //   <meta name=\"author\" content=\"Jatin Sharma\"></meta>\n    //   <meta name=\"robots\" content=\"index,follow\" />\n    //   <meta\n    //     name=\"keywords\"\n    //     content={`${keywords || \"\"} Jatin, Jatin sharma, j471n, j471n_`}\n    //   />\n\n    //   {/* Og */}\n    //   <meta property=\"og:title\" content={`${title || \"\"} Jatin Sharma`} />\n    //   <meta property=\"og:description\" content={description || \"Jatin Sharma\"} />\n    //   <meta property=\"og:site_name\" content=\"Jatin Sharma\" />\n    //   <meta property=\"og:url\" content={currentURL} key=\"ogurl\" />\n    //   <meta property=\"og:image\" content={previewImage || \"\"} />\n\n    //   {/* Twitter */}\n    //   <meta name=\"twitter:card\" content=\"summary_large_image\" />\n    //   <meta name=\"twitter:creator\" content=\"@j471n_\" />\n    //   <meta name=\"twitter:title\" content={`${title || \"\"} Jatin Sharma`} />\n    //   <meta name=\"twitter:description\" content={description} />\n    //   <meta name=\"twitter:image\" content={previewImage || \"\"} />\n    //   <meta name=\"twitter:image:alt\" content={title || \"Jatin Sharma\"}></meta>\n    //   <meta name=\"twitter:domain\" content={currentURL} />\n    // </Head>\n  );\n}\n"
  },
  {
    "path": "components/MovieCard.tsx",
    "content": "import React, { useState } from \"react\";\nimport { AiFillStar } from \"react-icons/ai\";\nimport { ITMDBData } from \"@lib/interface\";\nimport Image from \"next/image\";\nimport { motion } from \"framer-motion\";\n\nconst TMDB_IMAGE_PREFIX = \"https://image.tmdb.org/t/p/w780\";\n\nconst cardVariants = {\n  hidden: { opacity: 0, y: 12 },\n  visible: {\n    opacity: 1,\n    y: 0,\n    transition: { type: \"spring\" as const, stiffness: 160, damping: 22 },\n  },\n};\n\nexport default function MovieCard({ movie }: { movie: ITMDBData }) {\n  const [loading, setLoading] = useState(true);\n  const title = movie?.original_title ?? movie?.original_name ?? \"\";\n\n  return (\n    <motion.div\n      variants={cardVariants}\n      className=\"group flex-shrink-0 w-[148px] flex flex-col bg-white dark:bg-darkPrimary border border-gray-200 dark:border-neutral-700 hover:border-gray-400 dark:hover:border-gray-600 transition-colors duration-200\"\n    >\n      {/* Poster */}\n      <div className=\"relative w-full aspect-[2/3] overflow-hidden bg-gray-100 dark:bg-darkSecondary\">\n        {loading && (\n          <div className=\"absolute inset-0 bg-gray-200 dark:bg-neutral-700 animate-pulse\" />\n        )}\n        <Image\n          className=\"object-cover w-full h-full group-hover:scale-105 transition-transform duration-500\"\n          src={TMDB_IMAGE_PREFIX + movie.poster_path}\n          alt={title}\n          width={296}\n          height={444}\n          onLoadingComplete={() => setLoading(false)}\n        />\n      </div>\n\n      {/* Info */}\n      <div className=\"p-3 flex flex-col gap-2 flex-1\">\n        <WatchStatus rating={movie.rating} />\n        <p\n          className=\"text-xs font-medium text-gray-900 dark:text-white leading-snug line-clamp-2\"\n          title={title}\n        >\n          {title}\n        </p>\n      </div>\n    </motion.div>\n  );\n}\n\nfunction WatchStatus({ rating }: { rating?: number }) {\n  return (\n    <div className=\"flex items-center justify-between gap-1\">\n      {rating ? (\n        <>\n          <span className=\"font-mono text-[9px] tracking-[0.3em] uppercase text-emerald-700 dark:text-emerald-400 bg-emerald-50 dark:bg-emerald-900/30 px-2 py-0.5\">\n            Watched\n          </span>\n          <span className=\"flex items-center gap-0.5 text-[10px] font-mono text-gray-600 dark:text-gray-400\">\n            <AiFillStar className=\"w-3 h-3 text-amber-500\" />\n            {rating}\n          </span>\n        </>\n      ) : (\n        <span className=\"font-mono text-[9px] tracking-[0.3em] uppercase text-amber-700 dark:text-amber-400 bg-amber-50 dark:bg-amber-900/30 px-2 py-0.5 animate-pulse\">\n          Watching\n        </span>\n      )}\n    </div>\n  );\n}\n"
  },
  {
    "path": "components/Newsletter.tsx",
    "content": "import { useState } from \"react\";\nimport { ToastContainer, toast } from \"react-toastify\";\nimport { CgSpinnerTwo } from \"react-icons/cg\";\nimport { useDarkMode } from \"@context/darkModeContext\";\n\nexport default function Newsletter() {\n  const { isDarkMode } = useDarkMode();\n  const [email, setEmail] = useState(\"\");\n  const [validationLoading, setValidationLoading] = useState(false);\n\n  async function subscribeNewsLetter(e: React.FormEvent<HTMLFormElement>) {\n    e.preventDefault();\n    setValidationLoading(true);\n\n    fetch(\"/api/validate/email\", {\n      method: \"POST\",\n      body: JSON.stringify({ email }),\n    })\n      .then((res) => res.json())\n      .then((res) => {\n        if (res.status === \"success\" && res.valid === true) {\n          try {\n            fetch(process.env.NEXT_PUBLIC_EMAIL_LIST + email, {\n              mode: \"no-cors\",\n            });\n          } catch (error) {\n            console.error(error);\n          }\n          toast.success(\"You have been added to my mailing list.\");\n          setEmail(\"\");\n        } else {\n          toast.error(\"Please enter a valid email address.\");\n        }\n        setValidationLoading(false);\n      })\n      .catch((error) => {\n        console.error(error);\n        setValidationLoading(false);\n      });\n  }\n\n  return (\n    <>\n      <div className=\"not-prose my-12 border border-gray-200 dark:border-neutral-700 bg-white dark:bg-darkPrimary print:hidden\">\n        {/* Header */}\n        <div className=\"px-6 pt-6 pb-5 border-b border-gray-200 dark:border-neutral-700\">\n          <span className=\"font-mono text-[10px] tracking-[0.45em] uppercase text-gray-500 dark:text-gray-500 block mb-2\">\n            Newsletter\n          </span>\n          <p className=\"text-sm font-semibold text-gray-900 dark:text-white\">\n            Monthly digest on web dev, tech, and productivity.\n          </p>\n          <p className=\"mt-1 text-sm text-gray-600 dark:text-gray-400\">\n            No spam. Unsubscribe any time.\n          </p>\n        </div>\n\n        {/* Form */}\n        <form onSubmit={subscribeNewsLetter} className=\"flex p-4 gap-2\">\n          <input\n            type=\"email\"\n            name=\"email\"\n            value={email}\n            onChange={(e) => setEmail(e.target.value)}\n            placeholder=\"your@email.com\"\n            required\n            className=\"flex-1 min-w-0 px-3 py-2 text-sm bg-gray-50 dark:bg-darkSecondary border border-gray-200 dark:border-neutral-700 text-gray-900 dark:text-white placeholder:text-gray-400 dark:placeholder:text-gray-600 outline-none focus:border-gray-400 dark:focus:border-gray-600 transition-colors font-mono\"\n          />\n          <button\n            type=\"submit\"\n            disabled={validationLoading}\n            className=\"flex-shrink-0 flex items-center gap-2 px-4 py-2 bg-gray-900 dark:bg-white text-white dark:text-gray-900 font-mono text-[10px] tracking-[0.35em] uppercase hover:bg-gray-700 dark:hover:bg-gray-200 transition-colors disabled:opacity-50\"\n          >\n            {validationLoading ? (\n              <CgSpinnerTwo className=\"w-3.5 h-3.5 animate-spin\" />\n            ) : (\n              \"Subscribe\"\n            )}\n          </button>\n        </form>\n      </div>\n\n      <ToastContainer\n        theme={isDarkMode ? \"dark\" : \"light\"}\n        style={{ zIndex: 1000 }}\n        autoClose={3000}\n      />\n    </>\n  );\n}\n"
  },
  {
    "path": "components/OgImage.tsx",
    "content": "import Image from \"next/image\";\nfunction OgImage({ src, alt }: { src: string; alt: string }) {\n  return (\n    <div className=\"relative -mt-[35%] sm:-mt-0 md:-ml-[35%] w-full sm:w-1/2 md:w-8/12 shrink-0 rounded-xl overflow-hidden shadow-2xl before:absolute before:inset-0 dark:before:bg-black/20 before:z-10\">\n      <Image\n        title={alt}\n        alt={alt}\n        src={src}\n        width={1200}\n        height={630}\n        placeholder=\"blur\"\n        blurDataURL={src}\n        quality={25}\n        className=\"transition-all duration-300 lg:group-hover:scale-110 backdrop-blur-xl\"\n        style={{\n          width: \"100%\",\n          height: \"auto\",\n          objectFit: \"cover\",\n        }}\n      />\n    </div>\n  );\n}\n\nexport default OgImage;\n"
  },
  {
    "path": "components/PageHeader.tsx",
    "content": "import React from \"react\";\nimport { motion } from \"framer-motion\";\n\ninterface PageHeaderProps {\n  /** Large background watermark text, e.g. \"about\" or \"/uses\" */\n  watermark: string;\n  /** Mono eyebrow label above the title, e.g. \"Profile — 001\" */\n  eyebrow: string;\n  /** Page title rendered as h1 */\n  title: string;\n  /** Description rendered with left-border accent below the title */\n  description: string;\n  /** Content rendered inside the max-w-7xl container below the header */\n  children?: React.ReactNode;\n  /** Extra classes on the outermost wrapper — use for padding overrides */\n  className?: string;\n}\n\nexport default function PageHeader({\n  watermark,\n  eyebrow,\n  title,\n  description,\n  children,\n  className = \"\",\n}: PageHeaderProps) {\n  return (\n    <div\n      className={`relative w-full pt-20 px-6 sm:px-8 lg:px-12 overflow-hidden ${className}`}\n    >\n      {/* Watermark */}\n      <div\n        className=\"absolute -right-2 top-8 font-black select-none pointer-events-none leading-none tracking-tighter bg-gradient-to-b from-gray-300 to-gray-50 dark:from-[#232628] dark:to-darkPrimary bg-clip-text text-transparent\"\n        style={{ fontSize: \"clamp(5rem, 16vw, 13rem)\" }}\n        aria-hidden=\"true\"\n      >\n        {watermark}\n      </div>\n\n      <div className=\"relative max-w-7xl mx-auto z-10\">\n        {/* Header */}\n        <div className=\"space-y-4 mb-12 max-w-2xl\">\n          <motion.div\n            initial={{ opacity: 0, x: -16 }}\n            animate={{ opacity: 1, x: 0 }}\n            transition={{ duration: 0.4 }}\n            className=\"flex items-center gap-3\"\n          >\n            <div className=\"h-px w-5 bg-gray-400 dark:bg-gray-600 flex-shrink-0\" />\n            <span className=\"font-mono text-[10px] tracking-[0.45em] uppercase text-gray-500 dark:text-gray-500\">\n              {eyebrow}\n            </span>\n          </motion.div>\n\n          <motion.h1\n            initial={{ opacity: 0, y: 20 }}\n            animate={{ opacity: 1, y: 0 }}\n            transition={{ duration: 0.5, delay: 0.08 }}\n            className=\"text-4xl sm:text-5xl lg:text-6xl font-bold text-gray-900 dark:text-white\"\n          >\n            {title}\n          </motion.h1>\n\n          <motion.p\n            initial={{ opacity: 0, y: 16 }}\n            animate={{ opacity: 1, y: 0 }}\n            transition={{ duration: 0.45, delay: 0.14 }}\n            className=\"text-base text-gray-600 dark:text-gray-400 border-l-2 border-gray-300 dark:border-gray-700 pl-4 py-0.5\"\n          >\n            {description}\n          </motion.p>\n        </div>\n\n        {children}\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "components/PageNotFound.tsx",
    "content": "import Link from \"next/link\";\nimport MetaData from \"@components/MetaData\";\nimport { motion } from \"framer-motion\";\n\nexport default function PageNotFound() {\n  return (\n    <>\n      <MetaData\n        title=\"404\"\n        suffix=\"Page Not Found\"\n        description=\"You are lost in Space !!!\"\n      />\n\n      <div className=\"relative w-full min-h-screen pt-20 px-6 sm:px-8 lg:px-12 overflow-hidden flex items-center\">\n        {/* Watermark */}\n        <div\n          className=\"absolute -right-2 top-8 font-black select-none pointer-events-none leading-none tracking-tighter bg-gradient-to-b from-gray-300 to-gray-50 dark:from-[#232628] dark:to-darkPrimary bg-clip-text text-transparent\"\n          style={{ fontSize: \"clamp(5rem, 20vw, 16rem)\" }}\n          aria-hidden=\"true\"\n        >\n          404\n        </div>\n\n        <div className=\"relative mx-auto z-10 max-w-2xl space-y-6\">\n          <motion.div\n            initial={{ opacity: 0, x: -16 }}\n            animate={{ opacity: 1, x: 0 }}\n            transition={{ duration: 0.4 }}\n            className=\"flex items-center gap-3\"\n          >\n            <div className=\"h-px w-5 bg-gray-400 dark:bg-gray-600 flex-shrink-0\" />\n            <span className=\"font-mono text-[10px] tracking-[0.45em] uppercase text-gray-500 dark:text-gray-500\">\n              Error — 404\n            </span>\n          </motion.div>\n\n          <motion.h1\n            initial={{ opacity: 0, y: 20 }}\n            animate={{ opacity: 1, y: 0 }}\n            transition={{ duration: 0.5, delay: 0.08 }}\n            className=\"text-4xl sm:text-5xl lg:text-6xl font-bold text-gray-900 dark:text-white\"\n          >\n            Page not found.\n          </motion.h1>\n\n          <motion.p\n            initial={{ opacity: 0, y: 16 }}\n            animate={{ opacity: 1, y: 0 }}\n            transition={{ duration: 0.45, delay: 0.14 }}\n            className=\"text-base text-gray-600 dark:text-gray-400 border-l-2 border-gray-300 dark:border-gray-700 pl-4 py-0.5\"\n          >\n            You didn&apos;t break the internet, but I can&apos;t find what\n            you&apos;re looking for. Visit the homepage to get back on track.\n          </motion.p>\n\n          <motion.div\n            initial={{ opacity: 0, y: 12 }}\n            animate={{ opacity: 1, y: 0 }}\n            transition={{ duration: 0.4, delay: 0.22 }}\n          >\n            <Link\n              href=\"/\"\n              className=\"inline-flex items-center gap-2 px-5 py-2.5 bg-gray-900 dark:bg-white text-white dark:text-gray-900 font-mono text-[11px] tracking-[0.35em] uppercase transition-opacity hover:opacity-80\"\n            >\n              Take me home\n            </Link>\n          </motion.div>\n        </div>\n      </div>\n    </>\n  );\n}\n"
  },
  {
    "path": "components/PageTop.tsx",
    "content": "import {\n  fromLeftVariant,\n  opacityVariant,\n} from \"../content/FramerMotionVariants\";\nimport AnimatedHeading from \"./FramerMotion/AnimatedHeading\";\nimport AnimatedText from \"./FramerMotion/AnimatedText\";\n\nexport default function PageTop({\n  pageTitle,\n  headingClass,\n  containerClass,\n  children,\n}: {\n  pageTitle: string;\n  headingClass?: string;\n  containerClass?: string;\n  children?: React.ReactNode;\n}) {\n  return (\n    <div\n      className={`w-full flex flex-col gap-3 py-5 select-none mb-10 ${containerClass}`}\n    >\n      <AnimatedHeading\n        variants={fromLeftVariant}\n        className={`text-4xl  md:text-5xl font-bold text-neutral-900 dark:text-neutral-200 ${headingClass}`}\n      >\n        {pageTitle}\n      </AnimatedHeading>\n      <AnimatedText\n        variants={opacityVariant}\n        className=\"text-lg text-gray-600 dark:text-gray-400\"\n      >\n        {children}\n      </AnimatedText>\n    </div>\n  );\n}\n"
  },
  {
    "path": "components/Project.tsx",
    "content": "import { BsGithub } from \"react-icons/bs\";\nimport { MdOutlineLink } from \"react-icons/md\";\nimport Link from \"next/link\";\nimport Image from \"next/image\";\nimport { motion } from \"framer-motion\";\nimport { ProjectType } from \"@lib/types\";\n\nconst itemVariants = {\n  hidden: { opacity: 0, y: 16 },\n  visible: {\n    opacity: 1,\n    y: 0,\n    transition: { type: \"spring\", stiffness: 260, damping: 24 },\n  },\n};\n\nexport default function Project({\n  project,\n  featured = false,\n}: {\n  project: ProjectType;\n  featured?: boolean;\n}) {\n  return (\n    <motion.div\n      variants={itemVariants}\n      className={`flex border border-gray-200 dark:border-neutral-700 bg-white dark:bg-darkPrimary hover:bg-gray-50 dark:hover:bg-darkSecondary transition-colors group ${\n        featured\n          ? \"sm:col-span-2 lg:col-span-3 flex-col md:flex-row\"\n          : \"flex-col\"\n      }`}\n    >\n      {/* Cover image */}\n      {project.coverImage && (\n        <div\n          className={`relative overflow-hidden flex-shrink-0 border-gray-200 dark:border-neutral-700 ${\n            featured\n              ? \"w-full h-56 md:h-auto md:w-80 lg:w-[420px] border-b md:border-b-0 md:border-l order-first md:order-last\"\n              : \"w-full h-40 border-b\"\n          }`}\n        >\n          <Image\n            src={project.coverImage}\n            alt={project.name}\n            fill\n            quality={50}\n            placeholder=\"blur\"\n            blurDataURL={project.coverImage}\n            className=\"object-cover transition-transform duration-500 group-hover:scale-105\"\n          />\n        </div>\n      )}\n\n      {/* Content */}\n      <div\n        className={`flex flex-col flex-1 gap-3 ${\n          featured ? \"p-6 lg:p-8\" : \"p-4\"\n        }`}\n      >\n        {featured && (\n          <span className=\"font-mono text-[10px] tracking-[0.45em] uppercase text-gray-400 dark:text-gray-600\">\n            Featured\n          </span>\n        )}\n\n        <div className=\"flex-1 space-y-2\">\n          <h2\n            className={`font-bold text-gray-900 dark:text-white leading-snug ${\n              featured ? \"text-xl sm:text-2xl\" : \"text-sm\"\n            }`}\n          >\n            {project.name}\n          </h2>\n          <p\n            className={`text-sm text-gray-600 dark:text-gray-400 ${\n              featured ? \"line-clamp-4 max-w-xl\" : \"line-clamp-2\"\n            }`}\n          >\n            {project.description}\n          </p>\n        </div>\n\n        {/* Tech tags */}\n        {project.tools && project.tools.length > 0 && (\n          <div className=\"flex flex-wrap gap-1.5\">\n            {(featured ? project.tools : project.tools.slice(0, 4)).map(\n              (tool, index) => (\n                <span\n                  key={`${tool}-${index}`}\n                  className=\"font-mono text-[9px] tracking-[0.3em] uppercase px-2 py-0.5 border border-gray-200 dark:border-gray-700 text-gray-500 dark:text-gray-500\"\n                >\n                  {tool}\n                </span>\n              ),\n            )}\n          </div>\n        )}\n\n        {/* Links */}\n        <div className=\"flex items-center gap-4 pt-2 border-t border-gray-100 dark:border-neutral-700 mt-auto\">\n          <Link\n            href={project.githubURL}\n            title=\"Source Code on GitHub\"\n            target=\"_blank\"\n            rel=\"noopener noreferrer\"\n            className=\"flex items-center gap-1.5 font-mono text-[10px] tracking-[0.3em] uppercase text-gray-500 dark:text-gray-500 hover:text-gray-900 dark:hover:text-white transition-colors\"\n          >\n            <BsGithub className=\"w-3.5 h-3.5\" />\n            Code\n          </Link>\n\n          {project.previewURL && (\n            <Link\n              href={project.previewURL}\n              title=\"Live Preview\"\n              target=\"_blank\"\n              rel=\"noopener noreferrer\"\n              className=\"flex items-center gap-1.5 font-mono text-[10px] tracking-[0.3em] uppercase text-gray-500 dark:text-gray-500 hover:text-gray-900 dark:hover:text-white transition-colors\"\n            >\n              <MdOutlineLink className=\"w-3.5 h-3.5\" />\n              Preview\n            </Link>\n          )}\n        </div>\n      </div>\n    </motion.div>\n  );\n}\n"
  },
  {
    "path": "components/QRCodeContainer.tsx",
    "content": "import QRCode from \"react-qr-code\";\nimport useWindowLocation from \"@hooks/useWindowLocation\";\nimport { MdClose } from \"react-icons/md\";\nimport { AnimatePresence, motion } from \"framer-motion\";\nimport { useDarkMode } from \"@context/darkModeContext\";\n\nexport default function QRCodeContainer({\n  showQR,\n  setShowQR,\n}: {\n  showQR: boolean;\n  setShowQR: (value: boolean) => void;\n}) {\n  const { currentURL } = useWindowLocation();\n  const { isDarkMode } = useDarkMode();\n\n  function downloadQRCode() {\n    const svg = document.getElementById(\"QRCode\");\n    const svgData = new XMLSerializer().serializeToString(svg!);\n    const canvas = document.createElement(\"canvas\");\n    const ctx = canvas.getContext(\"2d\");\n    const img = new Image();\n    img.onload = () => {\n      canvas.width = img.width;\n      canvas.height = img.height;\n      ctx?.drawImage(img, 0, 0);\n      const pngFile = canvas.toDataURL(\"image/png\");\n      const downloadLink = document.createElement(\"a\");\n      downloadLink.download = \"QRCode\";\n      downloadLink.href = `${pngFile}`;\n      downloadLink.click();\n    };\n    img.src = `data:image/svg+xml;base64,${btoa(svgData)}`;\n  }\n\n  return (\n    <AnimatePresence>\n      {showQR && (\n        <>\n          {/* Backdrop */}\n          <motion.div\n            key=\"qr-backdrop\"\n            initial={{ opacity: 0 }}\n            animate={{ opacity: 1 }}\n            exit={{ opacity: 0 }}\n            onClick={() => setShowQR(false)}\n            className=\"fixed inset-0 bg-black/50 z-[9999]\"\n          />\n\n          {/* Panel */}\n          <motion.div\n            key=\"qr-panel\"\n            initial={{ opacity: 0, scale: 0.96, y: 12 }}\n            animate={{ opacity: 1, scale: 1, y: 0 }}\n            exit={{ opacity: 0, scale: 0.96, y: 12 }}\n            transition={{ type: \"spring\", stiffness: 340, damping: 30 }}\n            className=\"fixed inset-0 flex items-center justify-center z-[10000] pointer-events-none\"\n          >\n            <div className=\"pointer-events-auto w-full max-w-xs bg-white dark:bg-darkPrimary border border-gray-200 dark:border-neutral-700\">\n              {/* Header */}\n              <div className=\"h-0.5 w-full bg-gray-900 dark:bg-white\" />\n              <div className=\"flex items-center justify-between px-5 py-4 border-b border-gray-200 dark:border-neutral-700\">\n                <div>\n                  <span className=\"font-mono text-[10px] tracking-[0.45em] uppercase text-gray-400 dark:text-gray-500 block leading-none mb-1\">\n                    Share\n                  </span>\n                  <span className=\"text-sm font-semibold text-gray-900 dark:text-white\">\n                    Scan QR Code\n                  </span>\n                </div>\n                <button\n                  onClick={() => setShowQR(false)}\n                  aria-label=\"Close\"\n                  className=\"w-8 h-8 flex items-center justify-center border border-gray-200 dark:border-gray-700 text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:border-gray-900 dark:hover:border-white transition-colors\"\n                >\n                  <MdClose className=\"w-4 h-4\" />\n                </button>\n              </div>\n\n              {/* QR code */}\n              <div className=\"p-6 flex justify-center border-b border-gray-200 dark:border-neutral-700\">\n                <QRCode\n                  id=\"QRCode\"\n                  value={currentURL}\n                  size={180}\n                  bgColor={isDarkMode ? \"#1a1d1f\" : \"#ffffff\"}\n                  fgColor={isDarkMode ? \"#ffffff\" : \"#111827\"}\n                />\n              </div>\n\n              {/* URL + download */}\n              <div className=\"px-5 py-4 space-y-3\">\n                <p className=\"font-mono text-[10px] tracking-[0.2em] text-gray-400 dark:text-gray-500 truncate\">\n                  {currentURL}\n                </p>\n                <button\n                  onClick={downloadQRCode}\n                  className=\"w-full py-2.5 bg-gray-900 dark:bg-white text-white dark:text-gray-900 font-mono text-[10px] tracking-[0.35em] uppercase hover:opacity-80 transition-opacity\"\n                >\n                  Download PNG\n                </button>\n              </div>\n            </div>\n          </motion.div>\n        </>\n      )}\n    </AnimatePresence>\n  );\n}\n"
  },
  {
    "path": "components/SVG/Ditto.tsx",
    "content": "import { useDarkMode } from \"@context/darkModeContext\";\n\nexport default function Ditto() {\n  const { isDarkMode } = useDarkMode();\n\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"32\"\n      height=\"32\"\n      viewBox=\"0 0 32 32\"\n      fill=\"currentColor\"\n      className=\"utilities-svg\"\n    >\n      <rect width=\"32\" height=\"32\" rx=\"8\" fill=\"currentColor\" />\n      <g clipPath=\"url(#clip0_739_15)\">\n        <path\n          d=\"M18.691 17.0001C18.1726 17.0001 17.6542 17.0001 17.1357 17.0001C16.6173 17.0001 16.2007 17.0001 15.8859 17.0001C16.1081 16.0373 16.3303 15.0837 16.5525 14.1394C16.7747 13.2136 16.9968 12.2786 17.219 11.3343C17.4412 10.4085 17.6634 9.47347 17.8856 8.52917C18.2744 8.52917 18.6355 8.52917 18.9688 8.52917C19.3021 8.52917 19.6076 8.52917 19.8853 8.52917C20.163 8.52917 20.45 8.52917 20.7463 8.52917C21.024 8.52917 21.3388 8.52917 21.6906 8.52917C21.7646 8.52917 21.8295 8.53843 21.885 8.55694C21.922 8.59397 21.9405 8.631 21.9405 8.66804C21.9405 8.72358 21.9313 8.78839 21.9128 8.86245C21.598 9.69566 21.274 10.5566 20.9407 11.4454C20.6074 12.3342 20.2741 13.2322 19.9408 14.1394C19.5891 15.0652 19.265 15.954 18.9688 16.8057C18.9317 16.8983 18.8947 16.9538 18.8577 16.9723C18.8206 16.9909 18.7651 17.0001 18.691 17.0001ZM12.314 17.0001C11.7955 17.0001 11.2771 17.0001 10.7586 17.0001C10.2402 17.0001 9.8236 17.0001 9.50883 17.0001C9.73102 16.0373 9.95321 15.0837 10.1754 14.1394C10.3976 13.2136 10.6198 12.2786 10.842 11.3343C11.0642 10.4085 11.2863 9.47347 11.5085 8.52917C11.8974 8.52917 12.2584 8.52917 12.5917 8.52917C12.925 8.52917 13.2305 8.52917 13.5082 8.52917C13.786 8.52917 14.073 8.52917 14.3692 8.52917C14.6469 8.52917 14.9617 8.52917 15.3135 8.52917C15.3876 8.52917 15.4524 8.53843 15.5079 8.55694C15.545 8.59397 15.5635 8.631 15.5635 8.66804C15.5635 8.72358 15.5542 8.78839 15.5357 8.86245C15.2209 9.69566 14.8969 10.5566 14.5636 11.4454C14.2303 12.3342 13.8971 13.2322 13.5638 14.1394C13.212 15.0652 12.8879 15.954 12.5917 16.8057C12.5547 16.8983 12.5176 16.9538 12.4806 16.9723C12.4436 16.9909 12.388 17.0001 12.314 17.0001Z\"\n          fill={isDarkMode ? \"#25282a\" : \"#f2f5fa\"}\n        />\n      </g>\n      <defs>\n        <clipPath id=\"clip0_739_15\">\n          <rect\n            x=\"1.96216\"\n            y=\"3.16992\"\n            width=\"27.9245\"\n            height=\"24.4528\"\n            rx=\"0.754717\"\n            fill={isDarkMode ? \"#25282a\" : \"#f2f5fa\"}\n          />\n        </clipPath>\n      </defs>\n    </svg>\n  );\n}\n"
  },
  {
    "path": "components/SVG/Flameshot.tsx",
    "content": "import { useDarkMode } from \"@context/darkModeContext\";\n\nexport default function Flameshot({ className }: { className?: string }) {\n  const { isDarkMode } = useDarkMode();\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width={32}\n      height={32}\n      viewBox=\"0 0 32 32\"\n      fill=\"none\"\n      className={className}\n    >\n      <g clipPath=\"url(#clip0_1404_14)\">\n        <path\n          d=\"M16 0C24.85 0 32 7.15 32 16C32 24.85 24.85 32 16 32C7.15 32 0 24.85 0 16C0 7.15 7.15 0 16 0Z\"\n          fill=\"currentColor\"\n        />\n        <path\n          d=\"M17.1508 13.8819C26.4278 12.0912 26.7842 8.04529 26.7842 8.04529C26.7842 8.04529 26.0536 20.5842 19.2738 21.2476C10.6823 22.0882 9.39868 26.1413 9.39868 26.1413C9.39868 26.1413 11.6963 14.9347 17.1508 13.8819Z\"\n          fill={isDarkMode ? \"#666\" : \"#777\"}\n        />\n        <path\n          d=\"M11.5533 13.3286C19.414 9.99951 18.6941 4.56873 18.6941 4.56873C18.6941 4.56873 23.034 15.6479 16.5279 18.2308C9.25981 21.1161 9.58321 26.0448 9.58321 26.0448C9.58321 26.0448 6.93658 15.2838 11.5533 13.3286V13.3286Z\"\n          fill={isDarkMode ? \"#444\" : \"#999\"}\n        />\n        <path\n          d=\"M6.92249 16.2738C11.3389 10.4502 10.3455 6.36566 10.3455 6.36566C10.3455 6.36566 16.0039 13.4608 12.1536 17.8553C7.85232 22.7645 9.72553 26.1798 9.72553 26.1798C9.72553 26.1798 4.3894 19.6141 6.92249 16.2738V16.2738Z\"\n          fill={isDarkMode ? \"#000\" : \"#fff\"}\n        />\n      </g>\n      <defs>\n        <clipPath id=\"clip0_1404_14\">\n          <rect width={32} height={32} fill=\"white\" />\n        </clipPath>\n      </defs>\n    </svg>\n  );\n}\n"
  },
  {
    "path": "components/SVG/Flux.tsx",
    "content": "import { useDarkMode } from \"@context/darkModeContext\";\n\nexport default function Flux() {\n  const { isDarkMode } = useDarkMode();\n\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"32\"\n      height=\"32\"\n      viewBox=\"0 0 32 32\"\n      fill=\"currentColor\"\n      className=\"utilities-svg\"\n    >\n      <g clipPath=\"url(#clip0_741_34)\">\n        <path\n          d=\"M0 16C0 7.16344 7.16344 0 16 0V0C24.8366 0 32 7.16344 32 16V16C32 24.8366 24.8366 32 16 32V32C7.16344 32 0 24.8366 0 16V16Z\"\n          fill=\"currentColor\"\n        />\n        <circle\n          cx=\"22.6455\"\n          cy=\"17.5662\"\n          r=\"4.65609\"\n          fill=\"#9B9B9B\"\n          stroke={isDarkMode ? \"black\" : \"white\"}\n          strokeWidth=\"1\"\n        />\n        <path\n          d=\"M30.9635 28.1791C30.9635 34.0935 17.838 31.8614 11.9236 31.8614C6.00918 31.8614 1.3739 28.8871 1.3739 22.9727C-3.07865 20.8706 4.19449 12.6984 10.3075 12.6984C20.0853 12.6984 30.2863 23.5295 30.9635 28.1791Z\"\n          fill=\"#454545\"\n        />\n      </g>\n      <defs>\n        <clipPath id=\"clip0_741_34\">\n          <path\n            d=\"M0 16C0 7.16344 7.16344 0 16 0V0C24.8366 0 32 7.16344 32 16V16C32 24.8366 24.8366 32 16 32V32C7.16344 32 0 24.8366 0 16V16Z\"\n            fill=\"white\"\n          />\n        </clipPath>\n      </defs>\n    </svg>\n  );\n}\n"
  },
  {
    "path": "components/SVG/Logo.tsx",
    "content": "import { motion } from \"framer-motion\";\n\nexport default function Logo({ className }: { className: string }) {\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width={512}\n      height={512}\n      viewBox=\"0 0 512 512\"\n      fill=\"none\"\n      className={className}\n    >\n      <motion.path\n        initial={{ pathLength: 0 }}\n        animate={{ pathLength: 1 }}\n        transition={{ duration: 3, ease: \"easeInOut\" }}\n        d=\"M18 0V380.671L260.5 495.5L498 385.055V0M67 17.5H431V126H85V81\"\n        stroke=\"currentColor\"\n        strokeWidth=\"35\"\n      />\n      <motion.path\n        initial={{ pathLength: 0 }}\n        animate={{ pathLength: 1 }}\n        transition={{ duration: 3, ease: \"easeInOut\" }}\n        d=\"M67.5 327.5L258 416L431.5 341.5V264H84.5V183.5H449\"\n        stroke=\"currentColor\"\n        strokeWidth=\"35\"\n      />\n    </svg>\n  );\n}\n"
  },
  {
    "path": "components/SVG/MicrosoftToDo.tsx",
    "content": "export default function MicrosoftToDo() {\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"41\"\n      height=\"33\"\n      viewBox=\"0 0 41 33\"\n      fill=\"none\"\n      className=\"utilities-svg\"\n    >\n      <rect\n        width=\"22.3455\"\n        height=\"11.2534\"\n        rx=\"1.96723\"\n        transform=\"matrix(0.707135 0.707078 -0.707135 0.707078 8.14673 8.92383)\"\n        fill=\"#545454\"\n      />\n      <g filter=\"url(#filter0_d_744_7)\">\n        <rect\n          width=\"33.904\"\n          height=\"11.3527\"\n          rx=\"1.96723\"\n          transform=\"matrix(0.707135 -0.707078 0.707135 0.707078 8.07495 24.8127)\"\n          fill=\"currentColor\"\n        />\n      </g>\n      <defs>\n        <filter\n          id=\"filter0_d_744_7\"\n          x=\"8.103\"\n          y=\"0.474446\"\n          width=\"31.9466\"\n          height=\"31.9441\"\n          filterUnits=\"userSpaceOnUse\"\n          colorInterpolationFilters=\"sRGB\"\n        >\n          <feFlood floodOpacity=\"0\" result=\"BackgroundImageFix\" />\n          <feColorMatrix\n            in=\"SourceAlpha\"\n            type=\"matrix\"\n            values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0\"\n            result=\"hardAlpha\"\n          />\n          <feMorphology\n            radius=\"0.786893\"\n            operator=\"erode\"\n            in=\"SourceAlpha\"\n            result=\"effect1_dropShadow_744_7\"\n          />\n          <feOffset dy=\"-0.393446\" />\n          <feGaussianBlur stdDeviation=\"0.786893\" />\n          <feComposite in2=\"hardAlpha\" operator=\"out\" />\n          <feColorMatrix\n            type=\"matrix\"\n            values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0\"\n          />\n          <feBlend\n            mode=\"normal\"\n            in2=\"BackgroundImageFix\"\n            result=\"effect1_dropShadow_744_7\"\n          />\n          <feBlend\n            mode=\"normal\"\n            in=\"SourceGraphic\"\n            in2=\"effect1_dropShadow_744_7\"\n            result=\"shape\"\n          />\n        </filter>\n      </defs>\n    </svg>\n  );\n}\n"
  },
  {
    "path": "components/SVG/RainDrop.tsx",
    "content": "export default function RainDrop() {\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"32\"\n      height=\"35\"\n      viewBox=\"0 0 32 35\"\n      fill=\"currentColor\"\n      className=\"utilities-svg\"\n    >\n      <path\n        d=\"M13.3433 13.6108C17.1634 17.0095 15.8846 24.5064 15.8846 27.8684C10.8766 27.8684 7.307 28.3185 4.09012 26.3952C0.217283 24.0796 0.108804 18.6472 2.664 15.1169C5.21919 11.5867 10.1254 10.7479 13.3433 13.6108Z\"\n        fill=\"#454545\"\n      />\n      <path\n        d=\"M18.5482 13.7231C14.7622 17.0326 15.9116 24.5606 15.8844 27.8684C20.8117 27.9137 24.3683 28.2484 27.5489 26.3851C31.3781 24.1418 31.5288 18.7979 29.0434 15.3014C26.5579 11.8049 21.7374 10.9354 18.5482 13.7231Z\"\n        fill=\"#9F9F9F\"\n      />\n      <g filter=\"url(#filter0_d_745_19)\">\n        <path\n          d=\"M25.8466 13.5213C25.8466 20.3855 18.6544 24.6615 15.884 27.8685C12.4211 23.5925 5.92139 20.2167 5.92139 13.5213C5.92139 7.71055 10.3818 3 15.884 3C21.3862 3 25.8466 7.71055 25.8466 13.5213Z\"\n          fill=\"currentColor\"\n        />\n      </g>\n      <defs>\n        <filter\n          id=\"filter0_d_745_19\"\n          x=\"0.921387\"\n          y=\"0\"\n          width=\"29.9253\"\n          height=\"34.8687\"\n          filterUnits=\"userSpaceOnUse\"\n          colorInterpolationFilters=\"sRGB\"\n        >\n          <feFlood floodOpacity=\"0\" result=\"BackgroundImageFix\" />\n          <feColorMatrix\n            in=\"SourceAlpha\"\n            type=\"matrix\"\n            values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0\"\n            result=\"hardAlpha\"\n          />\n          <feOffset dy=\"2\" />\n          <feGaussianBlur stdDeviation=\"2.5\" />\n          <feComposite in2=\"hardAlpha\" operator=\"out\" />\n          <feColorMatrix\n            type=\"matrix\"\n            values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0\"\n          />\n          <feBlend\n            mode=\"normal\"\n            in2=\"BackgroundImageFix\"\n            result=\"effect1_dropShadow_745_19\"\n          />\n          <feBlend\n            mode=\"normal\"\n            in=\"SourceGraphic\"\n            in2=\"effect1_dropShadow_745_19\"\n            result=\"shape\"\n          />\n        </filter>\n      </defs>\n    </svg>\n  );\n}\n"
  },
  {
    "path": "components/SVG/ShareX.tsx",
    "content": "import { useDarkMode } from \"@context/darkModeContext\";\n\nexport default function ShareX() {\n  const { isDarkMode } = useDarkMode();\n\n  return (\n    <svg\n      width=\"32\"\n      height=\"32\"\n      viewBox=\"0 0 32 32\"\n      fill=\"currentColor\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      className=\"utilities-svg\"\n    >\n      <g clipPath=\"url(#clip0_736_2)\">\n        <path\n          d=\"M25.0435 10.8953C24.9357 6.27203 21.657 2.33881 17.0616 0.753423C15.6008 0.251362 14.0664 -0.00326983 12.5217 3.16972e-05C5.60626 3.16972e-05 5.88007e-08 4.98299 5.88007e-08 11.1305C-0.000141637 12.438 0.255807 13.7328 0.753391 14.9419C2.34226 10.3465 6.272 7.06786 10.8953 6.95655C10.9732 6.95655 11.0518 6.95655 11.1304 6.95655C13.8101 6.95655 16.2692 8.02229 18.1899 9.7976C18.6346 10.2098 19.0472 10.6554 19.424 11.1305C20.9489 13.0456 21.9576 15.4922 22.2031 18.1934C23.9777 16.2692 25.0435 13.8101 25.0435 11.1305C25.0435 11.0519 25.0435 10.9739 25.0435 10.8953Z\"\n          fill=\"currentColor\"\n        />\n        <path\n          d=\"M22.2024 17.842C21.9569 15.1408 20.9503 12.6942 19.4233 10.7791C19.0465 10.3053 18.6342 9.86083 18.1899 9.44969C16.2692 7.67438 13.8101 6.60864 11.1304 6.60864C11.0518 6.60864 10.9739 6.60864 10.8953 6.60864C6.272 6.71647 2.33878 9.99508 0.753391 14.5906C0.279533 13.4404 0.0240464 12.212 5.88007e-08 10.9683C5.88007e-08 11.0226 5.88007e-08 11.0761 5.88007e-08 11.1304C-0.000141637 12.4379 0.255807 13.7327 0.753391 14.9419C2.34226 10.3464 6.272 7.06777 10.8953 6.95647C10.9732 6.95647 11.0518 6.95647 11.1304 6.95647C13.8101 6.95647 16.2692 8.02221 18.1899 9.79751C18.6346 10.2097 19.0472 10.6553 19.424 11.1304C20.9489 13.0455 21.9576 15.4921 22.2031 18.1933C23.9777 16.2692 25.0435 13.81 25.0435 11.1304C25.0435 11.0761 25.0435 11.0226 25.0435 10.9683C24.9934 13.5763 23.9367 15.9659 22.2024 17.842Z\"\n          fill={isDarkMode ? \"#25282a\" : \"#f2f5fa\"}\n        />\n        <path\n          d=\"M6.95655 21.1047C6.95655 21.0268 6.95655 20.9482 6.95655 20.8696C6.95655 18.1899 8.02229 15.7308 9.7976 13.8101C11.8379 11.6021 14.8153 10.1044 18.1899 9.79759C16.2692 8.02228 13.8101 6.95654 11.1305 6.95654C11.0519 6.95654 10.9739 6.95654 10.8953 6.95654C6.27203 7.06437 2.33881 10.343 0.753423 14.9385C0.251362 16.3993 -0.00326983 17.9336 3.16972e-05 19.4783C3.16972e-05 26.3938 4.98299 32 11.1305 32C12.438 32.0002 13.7328 31.7442 14.9419 31.2466C10.3465 29.6578 7.06786 25.728 6.95655 21.1047Z\"\n          fill=\"currentColor\"\n        />\n        <path\n          d=\"M6.95647 21.1047C6.95647 21.0268 6.95647 20.9482 6.95647 20.8696C6.95647 18.1899 8.02221 15.7308 9.79751 13.8101C11.8379 11.6021 14.8153 10.1044 18.1899 9.79759C16.2692 8.02228 13.81 6.95654 11.1304 6.95654C11.0761 6.95654 11.0226 6.95654 10.9683 6.95654C13.5763 7.00454 15.9659 8.06124 17.842 9.7948C14.4674 10.1016 11.49 11.5993 9.44969 13.8073C7.67438 15.7308 6.60864 18.1899 6.60864 20.8696C6.60864 20.9482 6.60864 21.0261 6.60864 21.1047C6.71647 25.728 9.99508 29.6612 14.5906 31.2466C13.4404 31.7205 12.212 31.976 10.9683 32C11.0226 32 11.0761 32 11.1304 32C12.4379 32.0002 13.7327 31.7442 14.9419 31.2466C10.3464 29.6578 7.06777 25.728 6.95647 21.1047Z\"\n          fill={isDarkMode ? \"#25282a\" : \"#f2f5fa\"}\n        />\n        <path\n          d=\"M31.2466 17.0581C29.6578 21.6535 25.728 24.9321 21.1047 25.0434C21.0268 25.0434 20.9482 25.0434 20.8696 25.0434C18.1899 25.0434 15.7308 23.9777 13.8101 22.2024C11.6021 20.1621 10.1044 17.1847 9.79759 13.8101C8.02228 15.7308 6.95654 18.1899 6.95654 20.8695C6.95654 20.9481 6.95654 21.0261 6.95654 21.1047C7.06437 25.728 10.343 29.6612 14.9385 31.2466C16.3993 31.7486 17.9336 32.0033 19.4783 32C26.3938 32 32 27.017 32 20.8695C32.0002 19.562 31.7442 18.2672 31.2466 17.0581Z\"\n          fill=\"currentColor\"\n        />\n        <path\n          d=\"M9.79759 14.1579C10.1044 17.5325 11.6021 20.5099 13.8101 22.5502C15.7308 24.3255 18.1899 25.3913 20.8696 25.3913C20.9482 25.3913 21.0261 25.3913 21.1047 25.3913C25.728 25.2834 29.6612 22.0048 31.2466 17.4094C31.7205 18.5595 31.976 19.7879 32 21.0316C32 20.9774 32 20.9238 32 20.8695C32.0002 19.562 31.7442 18.2672 31.2466 17.0581C29.6578 21.6535 25.728 24.9321 21.1047 25.0434C21.0268 25.0434 20.9482 25.0434 20.8696 25.0434C18.1899 25.0434 15.7308 23.9777 13.8101 22.2024C11.6021 20.1621 10.1044 17.1847 9.79759 13.8101C8.02228 15.7308 6.95654 18.1899 6.95654 20.8695C6.95654 20.9238 6.95654 20.9774 6.95654 21.0316C7.00663 18.4236 8.06333 16.0341 9.79759 14.1579Z\"\n          fill={isDarkMode ? \"#25282a\" : \"#f2f5fa\"}\n        />\n        <path\n          d=\"M20.8695 5.88007e-08C19.562 -0.000141637 18.2672 0.255807 17.0581 0.753391C21.6535 2.34226 24.9321 6.272 25.0434 10.8953C25.0434 10.9732 25.0434 11.0518 25.0434 11.1304C25.0434 13.8101 23.9777 16.2692 22.2024 18.1899C20.1621 20.3979 17.1847 21.8957 13.8101 22.2024C15.7308 23.9777 18.1899 25.0435 20.8695 25.0435C20.9481 25.0435 21.0261 25.0435 21.1047 25.0435C25.728 24.9357 29.6612 21.657 31.2466 17.0616C31.7486 15.6008 32.0033 14.0664 32 12.5217C32 5.60626 27.017 5.88007e-08 20.8695 5.88007e-08Z\"\n          fill=\"currentColor\"\n        />\n        <path\n          d=\"M14.1579 22.2024C17.5325 21.8957 20.5099 20.3979 22.5502 18.1899C24.3255 16.2692 25.3913 13.8101 25.3913 11.1304C25.3913 11.0518 25.3913 10.9739 25.3913 10.8953C25.2834 6.272 22.0048 2.33878 17.4094 0.753391C18.5595 0.279533 19.7879 0.0240464 21.0316 5.88007e-08C20.9774 5.88007e-08 20.9238 5.88007e-08 20.8695 5.88007e-08C19.562 -0.000141637 18.2672 0.255807 17.0581 0.753391C21.6535 2.34226 24.9321 6.272 25.0434 10.8953C25.0434 10.9732 25.0434 11.0518 25.0434 11.1304C25.0434 13.8101 23.9777 16.2692 22.2024 18.1899C20.1621 20.3979 17.1847 21.8957 13.8101 22.2024C15.7308 23.9777 18.1899 25.0435 20.8695 25.0435C20.9238 25.0435 20.9774 25.0435 21.0316 25.0435C18.4236 24.9934 16.0341 23.9367 14.1579 22.2024Z\"\n          fill={isDarkMode ? \"#25282a\" : \"#f2f5fa\"}\n        />\n      </g>\n      <defs>\n        <clipPath id=\"clip0_736_2\">\n          <rect width=\"32\" height=\"32\" fill=\"currentColor\" />\n        </clipPath>\n      </defs>\n    </svg>\n  );\n}\n"
  },
  {
    "path": "components/SVG/UPI.tsx",
    "content": "import React from \"react\";\n\nexport default function UPI({ className }: { className: string }) {\n  return (\n    <svg\n      className={className}\n      width=\"80\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n      viewBox=\"0 0 1024 466\"\n    >\n      <path\n        fill=\"#3d3d3c\"\n        d=\"M98.1 340.7h6.3l-5.9 24.5c-.9 3.6-.7 6.4.5 8.2 1.2 1.8 3.4 2.7 6.7 2.7 3.2 0 5.9-.9 8-2.7 2.1-1.8 3.5-4.6 4.4-8.2l5.9-24.5h6.4l-6 25.1c-1.3 5.4-3.6 9.5-7 12.2-3.3 2.7-7.7 4.1-13.1 4.1-5.4 0-9.1-1.3-11.1-4s-2.4-6.8-1.1-12.2l6-25.2zm31.4 40.3 10-41.9 19 24.6c.5.7 1 1.4 1.5 2.2.5.8 1 1.7 1.6 2.7l6.7-27.9h5.9l-10 41.8-19.4-25.1-1.5-2.1c-.5-.8-.9-1.5-1.2-2.4l-6.7 28h-5.9zm44.2 0 9.6-40.3h6.4l-9.6 40.3h-6.4zm15.5 0 9.6-40.3h21.9l-1.3 5.6h-15.5l-2.4 10H217l-1.4 5.7h-15.5l-4.5 18.9h-6.4zm29 0 9.6-40.3h6.4l-9.6 40.3h-6.4zm15.5 0 9.6-40.3h21.9l-1.3 5.6h-15.5l-2.4 10.1h15.5l-1.4 5.7h-15.5l-3.1 13H257l-1.4 5.9h-21.9zm29.3 0 9.6-40.3h8.6c5.6 0 9.5.3 11.6.9 2.1.6 3.9 1.5 5.3 2.9 1.8 1.8 3 4.1 3.5 6.8.5 2.8.3 6-.5 9.5-.9 3.6-2.2 6.7-4 9.5-1.8 2.8-4.1 5-6.8 6.8-2 1.4-4.2 2.3-6.6 2.9-2.3.6-5.8.9-10.4.9H263zm7.8-6h5.4c2.9 0 5.2-.2 6.8-.6 1.6-.4 3-1.1 4.3-2 1.8-1.3 3.3-2.9 4.5-4.9 1.2-1.9 2.1-4.2 2.7-6.8.6-2.6.8-4.8.5-6.7-.3-1.9-1-3.6-2.2-4.9-.9-1-2-1.6-3.5-2-1.5-.4-3.8-.6-7.1-.6h-4.6l-6.8 28.5zm59.7-12.1-4.3 18.1h-6l9.6-40.3h9.7c2.9 0 4.9.2 6.2.5 1.3.3 2.3.8 3.1 1.6 1 .9 1.7 2.2 2 3.8.3 1.6.2 3.3-.2 5.2-.5 1.9-1.2 3.7-2.3 5.3-1.1 1.6-2.4 2.9-3.8 3.8-1.2.7-2.5 1.3-3.9 1.6-1.4.3-3.6.5-6.4.5h-3.7zm1.7-5.4h1.6c3.5 0 6-.4 7.4-1.2 1.4-.8 2.3-2.2 2.8-4.2.5-2.1.2-3.7-.8-4.5-1.1-.9-3.3-1.3-6.6-1.3H335l-2.8 11.2zm40.1 23.5-2-10.4h-15.6l-7 10.4H341l29-41.9 9 41.9h-6.7zm-13.8-15.9h10.9l-1.8-9.2c-.1-.6-.2-1.3-.2-2-.1-.8-.1-1.6-.1-2.5-.4.9-.8 1.7-1.3 2.5-.4.8-.8 1.5-1.2 2.1l-6.3 9.1zm29.7 15.9 4.4-18.4-8-21.8h6.7l5 13.7c.1.4.2.8.4 1.4.2.6.3 1.2.5 1.8l1.2-1.8c.4-.6.8-1.1 1.2-1.6l11.7-13.5h6.4L399 362.5l-4.4 18.4h-6.4zm60.9-19.9c0-.3.1-1.2.3-2.6.1-1.2.2-2.1.3-2.9-.4.9-.8 1.8-1.3 2.8-.5.9-1.1 1.9-1.8 2.8l-15.4 21.5-5-21.9c-.2-.9-.4-1.8-.5-2.6-.1-.8-.2-1.7-.2-2.5-.2.8-.5 1.7-.8 2.7-.3.9-.7 1.9-1.2 2.9l-9 19.8h-5.9l19.3-42 5.5 25.4c.1.4.2 1.1.3 2 .1.9.3 2.1.5 3.5.7-1.2 1.6-2.6 2.8-4.4.3-.5.6-.8.7-1.1l17.4-25.4-.6 42h-5.9l.5-20zm10.6 19.9 9.6-40.3h21.9l-1.3 5.6h-15.5l-2.4 10.1h15.5l-1.4 5.7h-15.5l-3.1 13H483l-1.4 5.9h-21.9zm29.2 0 10-41.9 19 24.6c.5.7 1 1.4 1.5 2.2.5.8 1 1.7 1.6 2.7l6.7-27.9h5.9l-10 41.8-19.4-25.1-1.5-2.1c-.5-.8-.9-1.5-1.2-2.4l-6.7 28h-5.9zm65.1-34.8-8.3 34.7h-6.4l8.3-34.7h-10.4l1.3-5.6h27.2l-1.3 5.6H554zm6.7 26.7 5.7-2.4c.1 1.8.6 3.2 1.7 4.1 1.1.9 2.6 1.4 4.6 1.4 1.9 0 3.5-.5 4.9-1.6 1.4-1.1 2.3-2.5 2.7-4.3.6-2.4-.8-4.5-4.2-6.3-.5-.3-.8-.5-1.1-.6-3.8-2.2-6.2-4.1-7.2-5.9-1-1.8-1.2-3.9-.6-6.4.8-3.3 2.5-5.9 5.2-8 2.7-2 5.7-3.1 9.3-3.1 2.9 0 5.2.6 6.9 1.7 1.7 1.1 2.6 2.8 2.9 4.9l-5.6 2.6c-.5-1.3-1.1-2.2-1.9-2.8-.8-.6-1.8-.9-3-.9-1.7 0-3.2.5-4.4 1.4-1.2.9-2 2.1-2.4 3.7-.6 2.4 1.1 4.7 5 6.8.3.2.5.3.7.4 3.4 1.8 5.7 3.6 6.7 5.4 1 1.8 1.2 3.9.6 6.6-.9 3.8-2.8 6.8-5.7 9.1-2.9 2.2-6.3 3.4-10.3 3.4-3.3 0-5.9-.8-7.7-2.4-2-1.6-2.9-3.9-2.8-6.8zm47.1 8.1 9.6-40.3h6.4l-9.6 40.3h-6.4zm15.6 0 10-41.9 19 24.6c.5.7 1 1.4 1.5 2.2.5.8 1 1.7 1.6 2.7l6.7-27.9h5.9l-10 41.8-19.4-25.1-1.5-2.1c-.5-.8-.9-1.5-1.2-2.4l-6.7 28h-5.9zm65.1-34.8-8.3 34.7h-6.4l8.3-34.7h-10.4l1.3-5.6h27.2l-1.3 5.6h-10.4zm6.9 34.8 9.6-40.3h22l-1.3 5.6h-15.5l-2.4 10.1h15.5l-1.4 5.7h-15.5l-3.1 13h15.5l-1.4 5.9h-22zm39.5-18.1-4.3 18h-6l9.6-40.3h8.9c2.6 0 4.6.2 5.9.5 1.4.3 2.5.9 3.3 1.7 1 1 1.6 2.2 1.9 3.8.3 1.5.2 3.2-.2 5.1-.8 3.2-2.1 5.8-4.1 7.6-2 1.8-4.5 2.9-7.5 3.3l9.1 18.3h-7.2l-8.7-18h-.7zm1.6-5.1h1.2c3.4 0 5.7-.4 7-1.2 1.3-.8 2.2-2.2 2.7-4.3.5-2.2.3-3.8-.7-4.7-1-.9-3.1-1.4-6.3-1.4h-1.2l-2.7 11.6zm18.9 23.2 9.6-40.3h21.9l-1.3 5.6h-15.5l-2.4 10h15.5l-1.4 5.7h-15.5l-4.5 18.9h-6.4zm52.8 0-2-10.4h-15.6l-7 10.4h-6.7l29-41.9 9 41.9h-6.7zm-13.9-15.9h10.9l-1.8-9.2c-.1-.6-.2-1.3-.2-2-.1-.8-.1-1.6-.1-2.5-.4.9-.8 1.7-1.3 2.5-.4.8-.8 1.5-1.2 2.1l-6.3 9.1zm62.2-14.6c-1.4-1.6-3.1-2.8-4.9-3.5-1.8-.8-3.8-1.2-6.1-1.2-4.3 0-8.1 1.4-11.5 4.2-3.4 2.8-5.6 6.5-6.7 11-1 4.3-.6 7.9 1.4 10.8 1.9 2.8 4.9 4.2 8.9 4.2 2.3 0 4.6-.4 6.9-1.3 2.3-.8 4.6-2.1 7-3.8l-1.8 7.4c-2 1.3-4.1 2.2-6.3 2.8-2.2.6-4.4.9-6.8.9-3 0-5.7-.5-8-1.5s-4.2-2.5-5.7-4.5c-1.5-1.9-2.4-4.2-2.8-6.8-.4-2.6-.3-5.4.5-8.4.7-3 1.9-5.7 3.5-8.3 1.6-2.6 3.7-4.9 6.1-6.8 2.4-2 5-3.5 7.8-4.5s5.6-1.5 8.5-1.5c2.3 0 4.4.3 6.4 1 1.9.7 3.7 1.7 5.3 3.1l-1.7 6.7zm.6 30.5 9.6-40.3h21.9l-1.3 5.6h-15.5l-2.4 10.1h15.5l-1.4 5.7H868l-3.1 13h15.5L879 381h-21.9z\"\n      />\n      <path\n        fill=\"#70706e\"\n        d=\"M740.7 305.6h-43.9l61-220.3h43.9l-61 220.3zM717.9 92.2c-3-4.2-7.7-6.3-14.1-6.3H462.6l-11.9 43.2h219.4l-12.8 46.1H481.8v-.1h-43.9l-36.4 131.5h43.9l24.4-88.2h197.3c6.2 0 12-2.1 17.4-6.3 5.4-4.2 9-9.4 10.7-15.6l24.4-88.2c1.9-6.6 1.3-11.9-1.7-16.1zm-342 199.6c-2.4 8.7-10.4 14.8-19.4 14.8H130.2c-6.2 0-10.8-2.1-13.8-6.3-3-4.2-3.7-9.4-1.9-15.6l55.2-198.8h43.9l-49.3 177.6h175.6l49.3-177.6h43.9l-57.2 205.9z\"\n      />\n      <path fill=\"#098041\" d=\"M877.5 85.7 933 196.1 816.3 306.5z\" />\n      <path fill=\"#e97626\" d=\"M838.5 85.7 894 196.1 777.2 306.5z\" />\n    </svg>\n  );\n}\n"
  },
  {
    "path": "components/SVG/Zip7.tsx",
    "content": "export default function Zip7() {\n  return (\n    <svg\n      xmlns=\"http://www.w3.org/2000/svg\"\n      width=\"32\"\n      height=\"32\"\n      viewBox=\"0 0 32 32\"\n      fill=\"none\"\n      className=\"utilities-svg\"\n    >\n      <g clipPath=\"url(#clip0_1332_2)\">\n        <path d=\"M32 4H0V29H32V4Z\" fill=\"none\" />\n        <path\n          d=\"M20.3636 13.5588V15.7647H24.2182L20.3636 21.4265V23.8529H26.9091V21.6471H23.0545L26.9091 15.9853V13.5588H20.3636ZM8 11.3529V13.5588H13.4545L10.1818 16.8676V21.6471H13.0909V16.8676L16 13.9265V11.3529M5.09091 9.14706H18.1818V23.8529H5.09091V9.14706ZM0 4V29H32V4H0ZM2.18182 6.20588H29.8182V26.7941H2.18182\"\n          fill=\"currentColor\"\n        />\n      </g>\n      <defs>\n        <clipPath id=\"clip0_1332_2\">\n          <rect\n            width=\"32\"\n            height=\"25\"\n            fill=\"white\"\n            transform=\"translate(0 4)\"\n          />\n        </clipPath>\n      </defs>\n    </svg>\n  );\n}\n"
  },
  {
    "path": "components/SVG/index.tsx",
    "content": "import Ditto from \"./Ditto\";\nimport Flux from \"./Flux\";\nimport MicrosoftToDo from \"./MicrosoftToDo\";\nimport RainDrop from \"./RainDrop\";\nimport ShareX from \"./ShareX\";\nimport Zip7 from \"./Zip7\";\nimport Flameshot from \"./Flameshot\";\n\nconst SVG = {\n  Ditto,\n  Flux,\n  MicrosoftToDo,\n  RainDrop,\n  ShareX,\n  Zip7,\n  Flameshot,\n};\nexport default SVG;\n"
  },
  {
    "path": "components/ScrollProgressBar.tsx",
    "content": "import { useCallback, useEffect, useState } from \"react\";\n\nexport default function ScrollProgressBar() {\n  const [scroll, setScroll] = useState(\"0\");\n\n  const progressBarHandler = useCallback(() => {\n    const totalScroll = document.documentElement.scrollTop;\n    const windowHeight =\n      document.documentElement.scrollHeight -\n      document.documentElement.clientHeight;\n    const scroll = `${totalScroll / windowHeight}`;\n\n    setScroll(scroll);\n  }, []);\n\n  useEffect(() => {\n    window.addEventListener(\"scroll\", progressBarHandler);\n    return () => window.removeEventListener(\"scroll\", progressBarHandler);\n  }, [progressBarHandler]);\n  return (\n    <div\n      className=\"!fixed left-0 w-full h-1 bg-black dark:bg-white origin-top-left  transform duration-300  top-[57px]\"\n      style={{\n        transform: `scale(${scroll},1)`,\n        zIndex: 100,\n      }}\n    />\n  );\n}\n"
  },
  {
    "path": "components/ScrollToTopButton.tsx",
    "content": "import { IoIosArrowUp } from \"react-icons/io\";\nimport { useEffect, useState } from \"react\";\nimport { motion, AnimatePresence } from \"framer-motion\";\nimport useScrollPercentage from \"@hooks/useScrollPercentage\";\n\nexport default function ScrollToTopButton() {\n  const [showButton, setShowButton] = useState(false);\n  const scrollPercentage = useScrollPercentage();\n\n  useEffect(() => {\n    setShowButton(scrollPercentage > 10 && scrollPercentage < 95);\n  }, [scrollPercentage]);\n\n  const scrollToTop = () => {\n    window.scrollTo({ top: 0, behavior: \"smooth\" });\n  };\n\n  return (\n    <AnimatePresence>\n      {showButton && (\n        <motion.button\n          initial={{ opacity: 0, y: 8 }}\n          animate={{ opacity: 1, y: 0 }}\n          exit={{ opacity: 0, y: 8 }}\n          transition={{ duration: 0.2 }}\n          onClick={scrollToTop}\n          aria-label=\"Scroll to top\"\n          className=\"fixed bottom-20 right-6 z-40 w-9 h-9 flex items-center justify-center bg-gray-900 dark:bg-white text-white dark:text-gray-900 hover:opacity-80 transition-opacity print:hidden\"\n        >\n          <IoIosArrowUp className=\"w-4 h-4\" />\n        </motion.button>\n      )}\n    </AnimatePresence>\n  );\n}\n"
  },
  {
    "path": "components/ShareOnSocialMedia.tsx",
    "content": "import \"react-toastify/dist/ReactToastify.css\";\n\nimport {\n  FacebookShareButton,\n  LinkedinShareButton,\n  TwitterShareButton,\n  WhatsappShareButton,\n} from \"react-share\";\nimport { FiCopy, FiLinkedin } from \"react-icons/fi\";\nimport { GrFacebookOption, GrTwitter } from \"react-icons/gr\";\n\nimport { BsThreeDots } from \"react-icons/bs\";\nimport { FaWhatsapp } from \"react-icons/fa\";\nimport { toast } from \"react-toastify\";\nimport useShare from \"../hooks/useShare\";\n\ntype Props = {\n  className?: string;\n  title: string;\n  url: string;\n  summary: string;\n  cover_image: string;\n  children?: React.ReactNode;\n};\n\nexport default function ShareOnSocialMedia({\n  title,\n  url,\n  summary,\n  cover_image,\n  children,\n}: Props) {\n  const { isShareSupported } = useShare();\n\n  async function handleShare() {\n    const blob = await fetch(cover_image).then((res) => res.blob());\n    const file = new File([blob], \"image.png\", { type: \"image/png\" });\n    if (window.navigator.share) {\n      window.navigator\n        .share({ title, text: summary, url, files: [file] })\n        .catch(console.error);\n    }\n  }\n\n  function copyTextToClipboard(text: string) {\n    if (!navigator.clipboard) {\n      toast.error(\"Clipboard not supported on this device.\");\n      return;\n    }\n    navigator.clipboard.writeText(text).then(\n      () => toast.success(\"Link copied!\"),\n      () => toast.error(\"Failed to copy link.\"),\n    );\n  }\n\n  const btnClass =\n    \"flex items-center gap-1.5 font-mono text-[10px] tracking-[0.3em] uppercase text-gray-500 dark:text-gray-500 hover:text-gray-900 dark:hover:text-white transition-colors px-3 py-2 border-r border-gray-200 dark:border-neutral-700 last:border-r-0\";\n\n  return (\n    <div className=\"not-prose flex flex-wrap border border-gray-200 dark:border-neutral-700\">\n      <FacebookShareButton quote={title} url={url} className=\"contents\">\n        <span className={btnClass}>\n          <GrFacebookOption className=\"w-3.5 h-3.5\" />\n          Facebook\n        </span>\n      </FacebookShareButton>\n\n      <TwitterShareButton\n        title={title}\n        url={url}\n        related={[\"@j471n_\"]}\n        className=\"contents\"\n      >\n        <span className={btnClass}>\n          <GrTwitter className=\"w-3.5 h-3.5\" />\n          Twitter\n        </span>\n      </TwitterShareButton>\n\n      <LinkedinShareButton\n        title={title}\n        summary={summary}\n        url={url}\n        source={url}\n        className=\"contents\"\n      >\n        <span className={btnClass}>\n          <FiLinkedin className=\"w-3.5 h-3.5\" />\n          LinkedIn\n        </span>\n      </LinkedinShareButton>\n\n      <WhatsappShareButton title={title} url={url} className=\"contents\">\n        <span className={btnClass}>\n          <FaWhatsapp className=\"w-3.5 h-3.5\" />\n          WhatsApp\n        </span>\n      </WhatsappShareButton>\n\n      <button onClick={() => copyTextToClipboard(url)} className={btnClass}>\n        <FiCopy className=\"w-3.5 h-3.5\" />\n        Copy link\n      </button>\n\n      {isShareSupported && (\n        <button onClick={handleShare} className={btnClass}>\n          <BsThreeDots className=\"w-3.5 h-3.5\" />\n          More\n        </button>\n      )}\n\n      {children}\n    </div>\n  );\n}\n"
  },
  {
    "path": "components/SnippetCard.tsx",
    "content": "import { motion } from \"framer-motion\";\nimport { ISnippet } from \"@lib/interface/sanity\";\nimport Image from \"next/image\";\nimport Link from \"next/link\";\n\nconst itemVariants = {\n  hidden: { opacity: 0, y: 12 },\n  visible: { opacity: 1, y: 0, transition: { duration: 0.35 } },\n};\n\nexport default function SnippetCard({ snippet }: { snippet: ISnippet }) {\n  return (\n    <motion.div variants={itemVariants}>\n      <Link\n        href={\"/snippets/\" + snippet.slug.current}\n        className=\"group flex flex-col gap-3 p-5 bg-white dark:bg-darkPrimary hover:bg-gray-50 dark:hover:bg-darkSecondary transition-colors h-full\"\n      >\n        {/* Language icon */}\n        <div className=\"w-9 h-9 flex items-center justify-center border border-gray-200 dark:border-neutral-700 group-hover:border-gray-400 dark:group-hover:border-gray-600 transition-colors flex-shrink-0\">\n          <Image\n            src={snippet.language.image.asset.url}\n            alt={snippet.language.name}\n            width={22}\n            height={22}\n            className=\"object-contain\"\n          />\n        </div>\n\n        {/* Language label */}\n        <span className=\"font-mono text-[10px] tracking-[0.4em] uppercase text-gray-400 dark:text-gray-500\">\n          {snippet.language.name}\n        </span>\n\n        {/* Title */}\n        <h2 className=\"text-sm font-semibold text-gray-900 dark:text-white leading-snug group-hover:underline underline-offset-2\">\n          {snippet.title}\n        </h2>\n\n        {/* Excerpt */}\n        <p className=\"text-sm text-gray-600 dark:text-gray-400 leading-relaxed line-clamp-2\">\n          {snippet.excerpt}\n        </p>\n      </Link>\n    </motion.div>\n  );\n}\n"
  },
  {
    "path": "components/SnowfallCanvas.tsx",
    "content": "// @ts-nocheck\n\nimport React, { useEffect, useRef } from \"react\";\n\nimport { useDarkMode } from \"@context/darkModeContext\";\nimport useWindowSize from \"@hooks/useWindowSize\";\n\nconst SnowfallCanvas: React.FC = () => {\n  const canvasRef = useRef<HTMLCanvasElement>(null);\n  const { width } = useWindowSize();\n\n  // useEffect(() => {\n  //   if (canvasRef.current) {\n  //     const c = canvasRef.current;\n  //     const $ = c.getContext(\"2d\");\n  //     let w = window.innerWidth;\n  //     let h = window.innerHeight;\n\n  //     const Snowy = () => {\n  //       let snow;\n  //       const arr = [];\n  //       const num =\n  //           width > 1000 ? 1000 : width < 1000 && width > 500 ? 500 : 100,\n  //         tsc = 1,\n  //         sp = 1;\n  //       const sc = 1.3,\n  //         mv = 20,\n  //         min = 1;\n\n  //       const Flake = function () {\n  //         this.draw = function () {\n  //           this.g = $.createRadialGradient(\n  //             this.x,\n  //             this.y,\n  //             0,\n  //             this.x,\n  //             this.y,\n  //             this.sz\n  //           );\n  //           this.g.addColorStop(0, \"rgba(255,255,255,1)\");\n  //           this.g.addColorStop(1, \"rgba(255,255,255,0)\");\n  //           $.moveTo(this.x, this.y);\n  //           $.fillStyle = this.g;\n  //           $.beginPath();\n  //           $.arc(this.x, this.y, this.sz, 0, Math.PI * 2, true);\n  //           $.fill();\n  //         };\n  //       };\n\n  //       for (let i = 0; i < num; ++i) {\n  //         snow = new Flake();\n  //         snow.y = Math.random() * (h + 50);\n  //         snow.x = Math.random() * w;\n  //         snow.t = Math.random() * (Math.PI * 2);\n  //         snow.sz = (100 / (10 + Math.random() * 100)) * sc;\n  //         snow.sp = Math.pow(snow.sz * 0.8, 2) * 0.15 * sp;\n  //         snow.sp = snow.sp < min ? min : snow.sp;\n  //         arr.push(snow);\n  //       }\n\n  //       const go = () => {\n  //         window.requestAnimationFrame(go);\n  //         $.clearRect(0, 0, w, h);\n  //         // $.fillStyle = \"rgba(0,0,0, 1)\"; // - add background color\n  //         $.fillRect(0, 0, w, h);\n  //         $.fill();\n\n  //         for (let i = 0; i < arr.length; ++i) {\n  //           const f = arr[i];\n  //           f.t += 0.05;\n  //           f.t = f.t >= Math.PI * 2 ? 0 : f.t;\n  //           f.y += f.sp;\n  //           f.x += Math.sin(f.t * tsc) * (f.sz * 0.3);\n  //           if (f.y > h + 50) f.y = -10 - Math.random() * mv;\n  //           if (f.x > w + mv) f.x = -mv;\n  //           if (f.x < -mv) f.x = w + mv;\n  //           f.draw();\n  //         }\n  //       };\n  //       go();\n  //     };\n\n  //     window.addEventListener(\n  //       \"resize\",\n  //       () => {\n  //         if (c) {\n  //           w = window.innerWidth;\n  //           h = window.innerHeight;\n  //           c.width = w;\n  //           c.height = h;\n  //         }\n  //       },\n  //       false\n  //     );\n\n  //     Snowy();\n  //   }\n  // }, []);\n\n  useEffect(() => {\n    let animationFrameId: number;\n    let ctx: CanvasRenderingContext2D | null;\n\n    const Snowy = () => {\n      if (canvasRef.current) {\n        ctx = canvasRef.current.getContext(\"2d\");\n        if (!ctx) return;\n\n        const w = window.innerWidth;\n        const h = window.innerHeight;\n        canvasRef.current.width = w;\n        canvasRef.current.height = h;\n\n        let snow;\n        const arr: any[] = [];\n        const num =\n          width > 1000 ? 1000 : width < 1000 && width > 500 ? 500 : 100;\n\n        const tsc = 1;\n        const sp = 1;\n        const sc = 1.3,\n          mv = 20,\n          min = 1;\n\n        const Flake = function () {\n          this.draw = function () {\n            if (!ctx) return;\n\n            this.g = ctx.createRadialGradient(\n              this.x,\n              this.y,\n              0,\n              this.x,\n              this.y,\n              this.sz,\n            );\n            this.g.addColorStop(0, \"hsla(255,255%,255%,1)\");\n            this.g.addColorStop(1, \"hsla(255,255%,255%,0)\");\n            ctx.moveTo(this.x, this.y);\n            ctx.fillStyle = this.g;\n            ctx.beginPath();\n            ctx.arc(this.x, this.y, this.sz, 0, Math.PI * 2, true);\n            ctx.fill();\n          };\n        };\n\n        for (let i = 0; i < num; ++i) {\n          snow = new Flake();\n          snow.y = Math.random() * (h + 50);\n          snow.x = Math.random() * w;\n          snow.t = Math.random() * (Math.PI * 2);\n          snow.sz = (100 / (10 + Math.random() * 100)) * sc;\n          snow.sp = Math.pow(snow.sz * 0.8, 2) * 0.15 * sp;\n          snow.sp = snow.sp < min ? min : snow.sp;\n          arr.push(snow);\n        }\n\n        const go = () => {\n          animationFrameId = window.requestAnimationFrame(go);\n          if (!ctx) return;\n\n          ctx.clearRect(0, 0, w, h);\n          // ctx.fillStyle = \"hsla(242, 95%, 3%, 1)\";\n          ctx.fillRect(0, 0, w, h);\n          ctx.fill();\n\n          for (let i = 0; i < arr.length; ++i) {\n            const f = arr[i];\n            f.t += 0.05;\n            f.t = f.t >= Math.PI * 2 ? 0 : f.t;\n            f.y += f.sp;\n            f.x += Math.sin(f.t * tsc) * (f.sz * 0.3);\n            if (f.y > h + 50) f.y = -10 - Math.random() * mv;\n            if (f.x > w + mv) f.x = -mv;\n            if (f.x < -mv) f.x = w + mv;\n            f.draw();\n          }\n        };\n\n        go();\n      }\n    };\n\n    Snowy();\n\n    return () => {\n      window.cancelAnimationFrame(animationFrameId);\n      if (ctx && canvasRef.current) {\n        ctx.clearRect(0, 0, canvasRef.current.width, canvasRef.current.height);\n      }\n    };\n  }, [width]);\n\n  const canvasStyles: React.CSSProperties = {\n    backgroundColor: \"transparent\",\n    margin: 0,\n    overflow: \"hidden\",\n    fontFamily: \"'Molle', cursive\",\n    position: \"absolute\",\n    top: \"0\",\n    left: \"0\",\n    zIndex: -1,\n  };\n\n  return (\n    <div\n      className=\"fixed inset-0\"\n      style={{\n        zIndex: 100000,\n        pointerEvents: \"none\",\n      }}\n    >\n      <canvas\n        className=\"w-full h-full\"\n        ref={canvasRef}\n        id=\"canv\"\n        style={canvasStyles}\n      ></canvas>\n    </div>\n  );\n};\n\nexport default SnowfallCanvas;\n"
  },
  {
    "path": "components/StaticPage.tsx",
    "content": "import { IStaticPage } from \"@lib/interface/sanity\";\nimport MDXComponents from \"@components/MDXComponents\";\nimport { MDXRemote } from \"next-mdx-remote\";\nimport MetaData from \"@components/MetaData\";\nimport { PageData } from \"@lib/types\";\nimport PageHeader from \"@components/PageHeader\";\n\nexport default function StaticPage({\n  metadata,\n  page,\n  showDescription = false,\n}: {\n  metadata: PageData;\n  page: IStaticPage;\n  showDescription?: boolean;\n}) {\n  return (\n    <>\n      <MetaData\n        title={metadata.title}\n        description={metadata.description}\n        previewImage={metadata.image}\n        keywords={metadata.keywords}\n      />\n\n      <PageHeader\n        watermark={page.title.toLowerCase()}\n        eyebrow={page.title}\n        title={page.title}\n        description={\n          showDescription ? metadata.description || page.excerpt : page.excerpt\n        }\n        className=\"pb-16\"\n      >\n        <div className=\"max-w-max font-barlow prose-typography\">\n          <MDXRemote\n            {...page.content}\n            frontmatter={{\n              slug: page.slug.current,\n              excerpt: page.excerpt,\n              title: page.title,\n              date: page.publishedAt,\n              keywords: page.keywords,\n              image: page.mainImage.asset.url,\n            }}\n            components={MDXComponents}\n          />\n        </div>\n      </PageHeader>\n    </>\n  );\n}\n"
  },
  {
    "path": "components/Stats/Artist.tsx",
    "content": "import Image from \"next/image\";\nimport Link from \"next/link\";\n\ntype ArtistProps = {\n  name: string;\n  url: string;\n  coverImage: string;\n  popularity: number;\n  id: number;\n};\n\nexport default function Artist({\n  name,\n  url,\n  coverImage,\n  popularity,\n  id,\n}: ArtistProps) {\n  return (\n    <Link\n      rel=\"noreferrer\"\n      target=\"_blank\"\n      href={url}\n      className=\"group flex items-center gap-4 p-4 border-b border-gray-200 dark:border-neutral-700 bg-white dark:bg-darkPrimary hover:bg-gray-50 dark:hover:bg-darkSecondary transition-colors duration-200 last:border-b-0\"\n    >\n      <span className=\"font-mono text-[10px] text-gray-400 dark:text-gray-600 w-5 text-right flex-shrink-0\">\n        {id + 1}\n      </span>\n      <div className=\"flex-shrink-0 w-10 h-10 overflow-hidden rounded-full border border-gray-200 dark:border-gray-700\">\n        <Image\n          src={coverImage}\n          width={40}\n          height={40}\n          alt={name}\n          quality={60}\n          className=\"w-full h-full object-cover\"\n        />\n      </div>\n      <div className=\"flex-1 min-w-0\">\n        <h2 className=\"text-sm font-semibold text-gray-900 dark:text-white truncate group-hover:text-gray-900 dark:group-hover:text-white\">\n          {name}\n        </h2>\n        <p className=\"text-xs text-gray-500 dark:text-gray-500 mt-0.5\">\n          Popularity: {popularity}\n        </p>\n      </div>\n    </Link>\n  );\n}\n"
  },
  {
    "path": "components/Stats/MonkeyTypeStats.tsx",
    "content": "import {\n  Area,\n  AreaChart,\n  Bar,\n  BarChart,\n  CartesianGrid,\n  ResponsiveContainer,\n  Tooltip,\n  TooltipProps,\n  XAxis,\n  YAxis,\n} from \"recharts\";\nimport {\n  NameType,\n  ValueType,\n} from \"recharts/types/component/DefaultTooltipContent\";\n\nimport { motion } from \"framer-motion\";\nimport React from \"react\";\nimport fetcher from \"@lib/fetcher\";\nimport { useDarkMode } from \"@context/darkModeContext\";\nimport useSWR from \"swr\";\n\n/* ── Types ── */\ninterface PersonalBest {\n  wpm: number;\n  acc: number;\n  consistency: number;\n  raw: number;\n  timestamp: number;\n  language: string;\n  difficulty: string;\n}\n\ninterface TrendPoint {\n  index: number;\n  wpm: number;\n  raw: number;\n  acc: number;\n  mode: string;\n  timestamp: number;\n}\n\ninterface MonkeyTypeData {\n  personalBests: {\n    time15: PersonalBest | null;\n    time30: PersonalBest | null;\n    time60: PersonalBest | null;\n  };\n  stats: {\n    completedTests: number;\n    startedTests: number;\n    timeTyping: number;\n  };\n  streak: {\n    length: number;\n    maxLength: number;\n  };\n  lastResult: any | null;\n  averages: {\n    wpm: number;\n    acc: number;\n    consistency: number;\n  } | null;\n  trendResults: TrendPoint[];\n  leaderboardRank: number | null;\n}\n\n/* ── Helpers ── */\nfunction formatTime(seconds: number): string {\n  const hrs = Math.floor(seconds / 3600);\n  const mins = Math.floor((seconds % 3600) / 60);\n  if (hrs > 0) return `${hrs}h ${mins}m`;\n  return `${mins}m`;\n}\n\nfunction formatDate(ts: number): string {\n  return new Date(ts).toLocaleDateString(\"en-US\", {\n    month: \"short\",\n    day: \"numeric\",\n    year: \"numeric\",\n  });\n}\n\n/* ── Animation variants ── */\nconst itemVariants = {\n  hidden: { opacity: 0, y: 10 },\n  visible: {\n    opacity: 1,\n    y: 0,\n    transition: { type: \"spring\" as const, stiffness: 160, damping: 22 },\n  },\n};\n\nconst containerVariants = {\n  hidden: {},\n  visible: { transition: { staggerChildren: 0.06 } },\n};\n\n/* ── Stat Card ── */\nfunction MonkeyStatsCard({\n  title,\n  value,\n}: {\n  title: string;\n  value: string | null | undefined;\n}) {\n  return (\n    <motion.div\n      variants={itemVariants}\n      className=\"group flex flex-col justify-center p-5 bg-white dark:bg-darkPrimary hover:bg-gray-50 dark:hover:bg-black/20 transition-colors duration-200\"\n    >\n      <p className=\"text-[10px] font-mono tracking-[0.35em] uppercase text-gray-500 dark:text-gray-500 mb-2\">\n        {title}\n      </p>\n      <div className=\"text-3xl font-black text-gray-900 dark:text-white leading-none\">\n        {value ?? (\n          <div className=\"h-7 w-20 bg-gray-200 dark:bg-neutral-700 animate-pulse\" />\n        )}\n      </div>\n    </motion.div>\n  );\n}\n\n/* ── Section heading ── */\nfunction SectionHeading({\n  eyebrow,\n  title,\n  description,\n}: {\n  eyebrow: string;\n  title: string;\n  description?: string;\n}) {\n  return (\n    <div className=\"mb-8 space-y-3\">\n      <motion.div\n        initial={{ opacity: 0, x: -16 }}\n        whileInView={{ opacity: 1, x: 0 }}\n        viewport={{ once: true }}\n        className=\"flex items-center gap-3\"\n      >\n        <div className=\"h-px w-5 bg-gray-400 dark:bg-gray-600 flex-shrink-0\" />\n        <span className=\"font-mono text-[10px] tracking-[0.45em] uppercase text-gray-500 dark:text-gray-500\">\n          {eyebrow}\n        </span>\n      </motion.div>\n      <motion.h2\n        initial={{ opacity: 0, y: 16 }}\n        whileInView={{ opacity: 1, y: 0 }}\n        viewport={{ once: true }}\n        transition={{ delay: 0.08 }}\n        className=\"text-2xl sm:text-3xl font-bold text-gray-900 dark:text-white\"\n      >\n        {title}\n      </motion.h2>\n      {description && (\n        <motion.p\n          initial={{ opacity: 0, y: 12 }}\n          whileInView={{ opacity: 1, y: 0 }}\n          viewport={{ once: true }}\n          transition={{ delay: 0.14 }}\n          className=\"text-sm text-gray-600 dark:text-gray-400 border-l-2 border-gray-300 dark:border-gray-700 pl-4 py-0.5 max-w-2xl\"\n        >\n          {description}\n        </motion.p>\n      )}\n    </div>\n  );\n}\n\n/* ── Chart section heading ── */\nfunction ChartSectionHeading({\n  title,\n  description,\n}: {\n  title: string;\n  description: string;\n}) {\n  return (\n    <div className=\"mb-6 space-y-2\">\n      <motion.div\n        initial={{ opacity: 0, x: -16 }}\n        whileInView={{ opacity: 1, x: 0 }}\n        viewport={{ once: true }}\n        className=\"flex items-center gap-3\"\n      >\n        <div className=\"h-px w-5 bg-gray-400 dark:bg-gray-600 flex-shrink-0\" />\n        <span className=\"font-mono text-[10px] tracking-[0.45em] uppercase text-gray-500 dark:text-gray-500\">\n          Typing\n        </span>\n      </motion.div>\n      <motion.h3\n        initial={{ opacity: 0, y: 12 }}\n        whileInView={{ opacity: 1, y: 0 }}\n        viewport={{ once: true }}\n        transition={{ delay: 0.06 }}\n        className=\"text-xl sm:text-2xl font-bold text-gray-900 dark:text-white\"\n      >\n        {title}\n      </motion.h3>\n      <motion.p\n        initial={{ opacity: 0, y: 8 }}\n        whileInView={{ opacity: 1, y: 0 }}\n        viewport={{ once: true }}\n        transition={{ delay: 0.12 }}\n        className=\"text-sm text-gray-600 dark:text-gray-400\"\n      >\n        {description}\n      </motion.p>\n    </div>\n  );\n}\n\n/* ── Personal best detail card ── */\nfunction PersonalBestCard({\n  mode,\n  pb,\n}: {\n  mode: string;\n  pb: PersonalBest | null | undefined;\n}) {\n  if (!pb) {\n    return (\n      <div className=\"bg-white dark:bg-darkPrimary border border-gray-200 dark:border-darkSecondary p-6\">\n        <p className=\"text-[10px] font-mono tracking-[0.35em] uppercase text-gray-500 dark:text-gray-500 mb-4\">\n          {mode}\n        </p>\n        <div className=\"space-y-3\">\n          {Array.from({ length: 4 }, (_, i) => (\n            <div\n              key={i}\n              className=\"h-4 w-full bg-gray-200 dark:bg-neutral-700 animate-pulse\"\n            />\n          ))}\n        </div>\n      </div>\n    );\n  }\n\n  const rows = [\n    { label: \"Raw WPM\", value: Math.round(pb.raw) },\n    { label: \"Accuracy\", value: `${Math.round(pb.acc * 100) / 100}%` },\n    {\n      label: \"Consistency\",\n      value: `${Math.round(pb.consistency * 100) / 100}%`,\n    },\n    { label: \"Language\", value: pb.language },\n    { label: \"Date\", value: formatDate(pb.timestamp) },\n  ];\n\n  return (\n    <motion.div\n      initial={{ opacity: 0, y: 16 }}\n      whileInView={{ opacity: 1, y: 0 }}\n      viewport={{ once: true }}\n      className=\"bg-white dark:bg-darkPrimary border border-gray-200 dark:border-darkSecondary\"\n    >\n      <div className=\"p-5 border-b border-gray-200 dark:border-darkSecondary\">\n        <p className=\"text-[10px] font-mono tracking-[0.35em] uppercase text-gray-500 dark:text-gray-500 mb-2\">\n          {mode}\n        </p>\n        <p className=\"text-4xl font-black text-gray-900 dark:text-white leading-none\">\n          {Math.round(pb.wpm)}\n          <span className=\"text-sm font-mono font-normal text-gray-400 dark:text-gray-500 ml-1.5\">\n            wpm\n          </span>\n        </p>\n      </div>\n      <div className=\"divide-y divide-gray-100 dark:divide-neutral-800\">\n        {rows.map((row) => (\n          <div\n            key={row.label}\n            className=\"flex items-center justify-between px-5 py-3\"\n          >\n            <span className=\"text-xs font-mono text-gray-500 dark:text-gray-500 uppercase tracking-wider\">\n              {row.label}\n            </span>\n            <span className=\"text-sm font-semibold text-gray-900 dark:text-white\">\n              {row.value}\n            </span>\n          </div>\n        ))}\n      </div>\n    </motion.div>\n  );\n}\n\n/* ── Tooltips ── */\nconst WpmTooltip = ({ active, payload }: TooltipProps<ValueType, NameType>) => {\n  if (active && payload && payload.length) {\n    return (\n      <div className=\"p-5 rounded-md bg-white dark:bg-darkSecondary text-black dark:text-gray-200 text-sm max-w-[250px] w-fit shadow-lg\">\n        <p>\n          <span className=\"font-medium\">Mode :</span> {payload[0].payload.mode}\n        </p>\n        <p>\n          <span className=\"font-medium\">WPM :</span> {payload[0].value}\n        </p>\n        <p>\n          <span className=\"font-medium\">Raw :</span> {payload[0].payload.raw}\n        </p>\n      </div>\n    );\n  }\n  return null;\n};\n\nconst AccuracyTooltip = ({\n  active,\n  payload,\n}: TooltipProps<ValueType, NameType>) => {\n  if (active && payload && payload.length) {\n    return (\n      <div className=\"p-5 rounded-md bg-white dark:bg-darkSecondary text-black dark:text-gray-200 text-sm max-w-[250px] w-fit shadow-lg\">\n        <p>\n          <span className=\"font-medium\">Mode :</span> {payload[0].payload.mode}\n        </p>\n        <p>\n          <span className=\"font-medium\">Accuracy :</span>{\" \"}\n          {payload[0].payload.acc}%\n        </p>\n        <p>\n          <span className=\"font-medium\">Consistency :</span>{\" \"}\n          {payload[0].payload.consistency}%\n        </p>\n      </div>\n    );\n  }\n  return null;\n};\n\nconst TrendTooltip = ({\n  active,\n  payload,\n}: TooltipProps<ValueType, NameType>) => {\n  if (active && payload && payload.length) {\n    return (\n      <div className=\"p-5 rounded-md bg-white dark:bg-darkSecondary text-black dark:text-gray-200 text-sm max-w-[250px] w-fit shadow-lg\">\n        <p>\n          <span className=\"font-medium\">Test :</span> #\n          {payload[0].payload.index}\n        </p>\n        <p>\n          <span className=\"font-medium\">WPM :</span> {payload[0].payload.wpm}\n        </p>\n        <p>\n          <span className=\"font-medium\">Raw :</span> {payload[0].payload.raw}\n        </p>\n        <p>\n          <span className=\"font-medium\">Acc :</span> {payload[0].payload.acc}%\n        </p>\n        <p>\n          <span className=\"font-medium\">Mode :</span> {payload[0].payload.mode}\n        </p>\n      </div>\n    );\n  }\n  return null;\n};\n\n/* ── Loading charts ── */\nfunction LoadingBarChart({\n  data,\n}: {\n  data: { mode: string; value: number }[];\n}) {\n  const { isDarkMode } = useDarkMode();\n  return (\n    <div className=\"pointer-events-none relative\">\n      <div className=\"grid place-items-center font-semibold text-base sm:text-lg absolute inset-0 z-1\">\n        Loading Data...\n      </div>\n      <BarChart\n        width={730}\n        height={250}\n        data={data}\n        margin={{ top: 10, right: 30, left: 0, bottom: 0 }}\n        style={{ opacity: \"0.25\" }}\n      >\n        <CartesianGrid\n          strokeDasharray=\"2 3\"\n          stroke={isDarkMode ? \"#ffffff20\" : \"#00000020\"}\n        />\n        <XAxis dataKey=\"mode\" />\n        <YAxis />\n        <Bar dataKey=\"value\" fill={isDarkMode ? \"#404040\" : \"#ababab\"} />\n      </BarChart>\n    </div>\n  );\n}\n\nfunction LoadingAreaChart() {\n  const { isDarkMode } = useDarkMode();\n  const placeholderData = Array.from({ length: 10 }, (_, i) => ({\n    index: i + 1,\n    wpm: 60 + Math.sin(i) * 20,\n  }));\n  return (\n    <div className=\"pointer-events-none relative\">\n      <div className=\"grid place-items-center font-semibold text-base sm:text-lg absolute inset-0 z-1\">\n        Loading Data...\n      </div>\n      <AreaChart\n        width={730}\n        height={300}\n        data={placeholderData}\n        margin={{ top: 10, right: 30, left: 0, bottom: 0 }}\n        style={{ opacity: \"0.25\" }}\n      >\n        <CartesianGrid\n          strokeDasharray=\"2 3\"\n          stroke={isDarkMode ? \"#ffffff20\" : \"#00000020\"}\n        />\n        <XAxis dataKey=\"index\" />\n        <YAxis />\n        <Area\n          type=\"monotone\"\n          dataKey=\"wpm\"\n          stroke={isDarkMode ? \"#d4d4d4\" : \"#404040\"}\n          fill={isDarkMode ? \"#404040\" : \"#d4d4d4\"}\n        />\n      </AreaChart>\n    </div>\n  );\n}\n\n/* ── Main Component ── */\nexport default function MonkeyTypeStats() {\n  const { isDarkMode } = useDarkMode();\n  const { data } = useSWR<MonkeyTypeData>(\"/api/stats/monkeytype\", fetcher, {\n    revalidateOnFocus: false,\n    revalidateOnReconnect: false,\n    dedupingInterval: 86400000,\n  });\n\n  const pb = data?.personalBests;\n  const stats = data?.stats;\n  const trendResults = data?.trendResults;\n\n  const allPbs = [pb?.time15, pb?.time30, pb?.time60].filter(\n    Boolean,\n  ) as PersonalBest[];\n  const bestWpm = allPbs.length\n    ? Math.round(Math.max(...allPbs.map((p) => p.wpm)))\n    : null;\n  const bestAcc = allPbs.length\n    ? Math.round(Math.max(...allPbs.map((p) => p.acc)) * 100) / 100\n    : null;\n  const completionRate =\n    stats && stats.startedTests > 0\n      ? Math.round((stats.completedTests / stats.startedTests) * 100)\n      : null;\n\n  const overviewStats: { title: string; value: string | null }[] = [\n    { title: \"Best WPM\", value: bestWpm != null ? `${bestWpm}` : null },\n    { title: \"Best Accuracy\", value: bestAcc != null ? `${bestAcc}%` : null },\n    {\n      title: \"Tests Completed\",\n      value: stats?.completedTests?.toLocaleString() ?? null,\n    },\n    {\n      title: \"Completion Rate\",\n      value: completionRate != null ? `${completionRate}%` : null,\n    },\n    {\n      title: \"Time Typing\",\n      value: stats ? formatTime(stats.timeTyping) : null,\n    },\n  ];\n\n  // Chart data\n  const wpmChartData = pb\n    ? [\n        {\n          mode: \"15 seconds\",\n          wpm: Math.round(pb.time15?.wpm ?? 0),\n          raw: Math.round(pb.time15?.raw ?? 0),\n        },\n        {\n          mode: \"30 seconds\",\n          wpm: Math.round(pb.time30?.wpm ?? 0),\n          raw: Math.round(pb.time30?.raw ?? 0),\n        },\n        {\n          mode: \"60 seconds\",\n          wpm: Math.round(pb.time60?.wpm ?? 0),\n          raw: Math.round(pb.time60?.raw ?? 0),\n        },\n      ]\n    : null;\n\n  const accChartData = pb\n    ? [\n        {\n          mode: \"15 seconds\",\n          acc: Math.round((pb.time15?.acc ?? 0) * 100) / 100,\n          consistency: Math.round((pb.time15?.consistency ?? 0) * 100) / 100,\n        },\n        {\n          mode: \"30 seconds\",\n          acc: Math.round((pb.time30?.acc ?? 0) * 100) / 100,\n          consistency: Math.round((pb.time30?.consistency ?? 0) * 100) / 100,\n        },\n        {\n          mode: \"60 seconds\",\n          acc: Math.round((pb.time60?.acc ?? 0) * 100) / 100,\n          consistency: Math.round((pb.time60?.consistency ?? 0) * 100) / 100,\n        },\n      ]\n    : null;\n\n  const loadingData = [\n    { mode: \"15 seconds\", value: 80 },\n    { mode: \"30 seconds\", value: 100 },\n    { mode: \"60 seconds\", value: 120 },\n  ];\n\n  return (\n    <>\n      {/* ══════════════════════════════════════════════ */}\n      {/* Section 1 — Overview Stats Grid               */}\n      {/* ══════════════════════════════════════════════ */}\n      <div className=\"mb-20\">\n        <SectionHeading\n          eyebrow=\"Monkeytype\"\n          title=\"Typing Performance\"\n          description=\"Personal typing statistics and records from Monkeytype, refreshed daily.\"\n        />\n\n        <motion.div\n          variants={containerVariants}\n          initial=\"hidden\"\n          whileInView=\"visible\"\n          viewport={{ once: true, margin: \"-60px\" }}\n          className=\"grid grid-cols-2 sm:grid-cols-3 xl:grid-cols-5 gap-px bg-gray-200 dark:bg-darkSecondary border border-gray-200 dark:border-darkSecondary\"\n        >\n          {overviewStats.map((stat, index) => (\n            <MonkeyStatsCard\n              key={index}\n              title={stat.title}\n              value={stat.value}\n            />\n          ))}\n        </motion.div>\n      </div>\n\n      {/* ══════════════════════════════════════════════ */}\n      {/* Section 2 — Personal Bests Detail Cards       */}\n      {/* ══════════════════════════════════════════════ */}\n      <div className=\"mb-20\">\n        <SectionHeading\n          eyebrow=\"Personal Bests\"\n          title=\"Records by Mode\"\n          description=\"Detailed breakdown of my best typing test results across 15, 30, and 60 second timed modes.\"\n        />\n\n        <div className=\"grid grid-cols-1 sm:grid-cols-3 gap-4\">\n          <PersonalBestCard mode=\"15 Seconds\" pb={pb?.time15} />\n          <PersonalBestCard mode=\"30 Seconds\" pb={pb?.time30} />\n          <PersonalBestCard mode=\"60 Seconds\" pb={pb?.time60} />\n        </div>\n      </div>\n\n      {/* ══════════════════════════════════════════════ */}\n      {/* Section 3 — WPM Trend (full-width area chart) */}\n      {/* ══════════════════════════════════════════════ */}\n      <div className=\"mb-20\">\n        <ChartSectionHeading\n          title=\"WPM Trend\"\n          description=\"Words per minute across my last 30 tests, in chronological order.\"\n        />\n        <ResponsiveContainer width=\"100%\" height={300}>\n          {trendResults && trendResults.length > 0 ? (\n            <AreaChart\n              data={trendResults}\n              margin={{ top: 10, right: 30, left: 0, bottom: 0 }}\n            >\n              <defs>\n                <linearGradient id=\"wpmGradient\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n                  <stop offset=\"5%\" stopColor=\"#e2b714\" stopOpacity={0.4} />\n                  <stop offset=\"95%\" stopColor=\"#e2b714\" stopOpacity={0} />\n                </linearGradient>\n              </defs>\n              <CartesianGrid\n                strokeDasharray=\"2 3\"\n                stroke={isDarkMode ? \"#ffffff20\" : \"#00000020\"}\n              />\n              <XAxis\n                dataKey=\"index\"\n                axisLine={false}\n                tickLine={false}\n                tick={{\n                  fontSize: 12,\n                  fill: isDarkMode ? \"#737373\" : \"#6b7280\",\n                }}\n              />\n              <YAxis\n                axisLine={false}\n                tickLine={false}\n                tick={{\n                  fontSize: 12,\n                  fill: isDarkMode ? \"#737373\" : \"#6b7280\",\n                }}\n              />\n              <Tooltip\n                content={<TrendTooltip />}\n                cursor={{\n                  stroke: isDarkMode ? \"#ffffff30\" : \"#00000020\",\n                  strokeWidth: 1,\n                }}\n              />\n              <Area\n                type=\"monotone\"\n                dataKey=\"wpm\"\n                stroke=\"#e2b714\"\n                strokeWidth={2}\n                fill=\"url(#wpmGradient)\"\n                dot={{\n                  r: 3,\n                  fill: \"#e2b714\",\n                  strokeWidth: 0,\n                }}\n                activeDot={{\n                  r: 5,\n                  fill: \"#f59e0b\",\n                  strokeWidth: 0,\n                }}\n              />\n            </AreaChart>\n          ) : (\n            <LoadingAreaChart />\n          )}\n        </ResponsiveContainer>\n      </div>\n\n      {/* ══════════════════════════════════════════════ */}\n      {/* Section 4 — PB Charts (2-col grid)            */}\n      {/* ══════════════════════════════════════════════ */}\n      <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-8\">\n        <div className=\"max-w-full\">\n          <ChartSectionHeading\n            title=\"Speed by Mode\"\n            description=\"Best words per minute across timed modes.\"\n          />\n          <ResponsiveContainer width=\"100%\" height={300}>\n            {wpmChartData ? (\n              <BarChart\n                width={730}\n                height={250}\n                data={wpmChartData}\n                margin={{ top: 10, right: 30, left: 0, bottom: 0 }}\n              >\n                <CartesianGrid\n                  strokeDasharray=\"2 3\"\n                  stroke={isDarkMode ? \"#ffffff20\" : \"#00000020\"}\n                />\n                <XAxis dataKey=\"mode\" />\n                <YAxis />\n                <Tooltip\n                  content={<WpmTooltip />}\n                  cursor={{ fill: \"transparent\" }}\n                />\n                <Bar dataKey=\"wpm\" fill=\"#e2b714\" radius={[3, 3, 0, 0]} />\n              </BarChart>\n            ) : (\n              <LoadingBarChart data={loadingData} />\n            )}\n          </ResponsiveContainer>\n        </div>\n\n        <div className=\"max-w-full\">\n          <ChartSectionHeading\n            title=\"Accuracy & Consistency\"\n            description=\"Accuracy and consistency across timed modes.\"\n          />\n          <ResponsiveContainer width=\"100%\" height={300}>\n            {accChartData ? (\n              <BarChart\n                width={730}\n                height={250}\n                data={accChartData}\n                margin={{ top: 10, right: 30, left: 0, bottom: 0 }}\n              >\n                <CartesianGrid\n                  strokeDasharray=\"2 3\"\n                  stroke={isDarkMode ? \"#ffffff20\" : \"#00000020\"}\n                />\n                <XAxis dataKey=\"mode\" />\n                <YAxis domain={[0, 100]} />\n                <Tooltip\n                  content={<AccuracyTooltip />}\n                  cursor={{ fill: \"transparent\" }}\n                />\n                <Bar\n                  dataKey=\"acc\"\n                  fill=\"#22c55e\"\n                  name=\"Accuracy\"\n                  radius={[3, 3, 0, 0]}\n                />\n                <Bar\n                  dataKey=\"consistency\"\n                  fill=\"#3b82f6\"\n                  name=\"Consistency\"\n                  radius={[3, 3, 0, 0]}\n                />\n              </BarChart>\n            ) : (\n              <LoadingBarChart data={loadingData} />\n            )}\n          </ResponsiveContainer>\n        </div>\n      </div>\n    </>\n  );\n}\n"
  },
  {
    "path": "components/Stats/StatsCard.tsx",
    "content": "import { motion } from \"framer-motion\";\n\nconst itemVariants = {\n  hidden: { opacity: 0, y: 10 },\n  visible: {\n    opacity: 1,\n    y: 0,\n    transition: { type: \"spring\" as const, stiffness: 160, damping: 22 },\n  },\n};\n\nexport default function StatsCard({\n  title,\n  value,\n}: {\n  title: string;\n  value: string;\n}) {\n  return (\n    <motion.div\n      variants={itemVariants}\n      className=\"group flex flex-col justify-center p-5 bg-white dark:bg-darkPrimary hover:bg-gray-50 dark:hover:bg-black/20 transition-colors duration-200\"\n    >\n      <p className=\"text-[10px] font-mono tracking-[0.35em] uppercase text-gray-500 dark:text-gray-500 mb-2\">\n        {title}\n      </p>\n      <div className=\"text-3xl font-black text-gray-900 dark:text-white leading-none\">\n        {value ?? (\n          <div className=\"h-7 w-20 bg-gray-200 dark:bg-neutral-700 animate-pulse\" />\n        )}\n      </div>\n    </motion.div>\n  );\n}\n"
  },
  {
    "path": "components/Stats/Track.tsx",
    "content": "import Image from \"next/image\";\nimport Link from \"next/link\";\n\ntype TrackProps = {\n  url: string;\n  title: string;\n  artist: string;\n  coverImage: string;\n  id: number;\n};\n\nexport default function Track({\n  url,\n  title,\n  artist,\n  coverImage,\n  id,\n}: TrackProps) {\n  return (\n    <Link\n      href={url}\n      rel=\"noreferrer\"\n      target=\"_blank\"\n      className=\"group flex items-center gap-4 p-4 border-b border-gray-200 dark:border-neutral-700 bg-white dark:bg-darkPrimary hover:bg-gray-50 dark:hover:bg-darkSecondary transition-colors duration-200 last:border-b-0\"\n    >\n      <span className=\"font-mono text-[10px] text-gray-400 dark:text-gray-600 w-5 text-right flex-shrink-0\">\n        {id + 1}\n      </span>\n      <div className=\"flex-shrink-0 w-10 h-10 overflow-hidden border border-gray-200 dark:border-gray-700\">\n        {coverImage ? (\n          <Image\n            src={coverImage}\n            width={40}\n            height={40}\n            alt={title}\n            quality={60}\n            className=\"w-full h-full object-cover\"\n          />\n        ) : (\n          <div className=\"w-full h-full bg-gray-200 dark:bg-neutral-700 animate-pulse\" />\n        )}\n      </div>\n      <div className=\"flex-1 min-w-0\">\n        <p className=\"text-sm font-semibold text-gray-900 dark:text-white truncate group-hover:text-gray-900 dark:group-hover:text-white\">\n          {title}\n        </p>\n        <p className=\"text-xs text-gray-500 dark:text-gray-500 truncate mt-0.5\">\n          {artist}\n        </p>\n      </div>\n    </Link>\n  );\n}\n"
  },
  {
    "path": "components/Support.tsx",
    "content": "import UPI from \"@components/SVG/UPI\";\nimport {\n  FadeContainer,\n  fromTopVariant,\n  popUp,\n} from \"@content/FramerMotionVariants\";\nimport support from \"@content/support\";\nimport Link from \"next/link\";\nimport React, { FormEvent, useState } from \"react\";\nimport { motion, AnimatePresence } from \"framer-motion\";\nimport { useDarkMode } from \"@context/darkModeContext\";\nimport QRCode from \"react-qr-code\";\nimport { BiRupee } from \"react-icons/bi\";\nimport { IoMdArrowRoundBack } from \"react-icons/io\";\nimport { FiInfo } from \"react-icons/fi\";\nimport { lockScroll, removeScrollLock } from \"@utils/functions\";\nimport AnimatedDiv from \"@components/FramerMotion/AnimatedDiv\";\n\nexport default function Support() {\n  const [showUPIForm, setShowUPIForm] = useState(false);\n\n  return (\n    <section>\n      <h3 className=\"my-5 text-2xl font-bold\">Support me 💪</h3>\n\n      <AnimatedDiv\n        variants={FadeContainer}\n        className=\"grid gap-5 sm:grid-cols-3\"\n      >\n        {support.map((paymentMethod) => {\n          return (\n            <Link\n              target=\"_blank\"\n              rel=\"noopener noreferrer\"\n              key={paymentMethod.name}\n              href={paymentMethod.url}\n            >\n              <motion.p\n                variants={popUp}\n                className=\"grid p-5 duration-200 bg-white shadow text-darkSecondary dark:bg-darkSecondary dark:text-gray-300 place-items-center group rounded-xl hover:ring-1 ring-gray-500 active:ring\"\n              >\n                <div className=\"flex flex-col items-center gap-5 select-none\">\n                  <paymentMethod.Icon className=\"text-3xl duration-150 group-hover:lg:scale-150 \" />\n\n                  <p className=\"text-sm font-semibold\">{paymentMethod.name}</p>\n                </div>\n              </motion.p>\n            </Link>\n          );\n        })}\n        <motion.button\n          variants={popUp}\n          onClick={() => {\n            setShowUPIForm(!showUPIForm);\n            lockScroll();\n          }}\n          className=\"grid p-5 duration-200 bg-white shadow text-darkSecondary dark:bg-darkSecondary dark:text-gray-300 place-items-center group rounded-xl hover:ring-1 ring-gray-500 active:ring\"\n        >\n          <div className=\"flex flex-col items-center gap-5 select-none\">\n            <UPI className=\"text-3xl duration-150 group-hover:lg:scale-150 \" />\n            <p className=\"text-sm font-semibold\">UPI</p>\n          </div>\n        </motion.button>\n      </AnimatedDiv>\n      <AnimatePresence>\n        {showUPIForm && (\n          <UPIPaymentForm\n            close={() => {\n              setShowUPIForm(false);\n              removeScrollLock();\n            }}\n          />\n        )}\n      </AnimatePresence>\n    </section>\n  );\n}\n\nfunction UPIPaymentForm({ close }: { close: () => void }) {\n  const [amount, setAmount] = useState(0);\n  const [qrValue, setQrValue] = useState(\"\");\n\n  const { isDarkMode } = useDarkMode();\n\n  const generatePaymentQR = (e: FormEvent) => {\n    e.preventDefault();\n    setQrValue(\n      `upi://pay?pa=${process.env.NEXT_PUBLIC_UPI}&pn=Jatin%20Sharma&am=${amount}&purpose=nothing&cu=INR`\n    );\n  };\n\n  return (\n    <motion.div\n      initial=\"hidden\"\n      animate=\"visible\"\n      exit=\"hidden\"\n      variants={FadeContainer}\n      className=\"fixed inset-0 z-50 grid bg-black/70 place-items-center\"\n    >\n      <motion.div\n        initial=\"hidden\"\n        animate=\"visible\"\n        variants={fromTopVariant}\n        exit=\"hidden\"\n        className=\"m-5 w-[90%] relative  rounded-lg px-5 py-5 max-w-md bg-white dark:bg-darkSecondary\"\n      >\n        <button title=\"Back\" onClick={close}>\n          <IoMdArrowRoundBack className=\"m-0 icon\" />\n        </button>\n\n        {!qrValue ? (\n          <>\n            <form\n              onSubmit={generatePaymentQR}\n              className=\"flex flex-col gap-5 my-5 mb-10\"\n            >\n              <div className=\"relative flex items-center justify-center\">\n                <BiRupee className=\"h-full -ml-1 text-gray-600 w-9 dark:text-gray-200\" />\n                <input\n                  onInput={(e: React.ChangeEvent<HTMLInputElement>) => {\n                    if (e.target.value.length === 0)\n                      return (e.target.style.width = \"3ch\");\n                    if ((e.target as HTMLInputElement).value.length > 7) return;\n                    e.target.style.width = e.target.value.length + \"ch\";\n                  }}\n                  title=\"Enter amount\"\n                  id=\"amount\"\n                  className=\"bg-transparent rounded-lg dark:placeholder-gray-400 text-gray-600 dark:text-gray-200 outline-none font-bold text-2xl w-[3ch]\"\n                  type=\"number\"\n                  name=\"amount\"\n                  placeholder=\"500\"\n                  min=\"100\"\n                  max=\"10000\"\n                  required\n                  value={amount}\n                  onChange={(e) => setAmount(parseInt(e.target.value))}\n                />\n              </div>\n              <input type=\"submit\" value=\"\" hidden />\n            </form>\n\n            {amount >= 100 && (\n              <motion.button\n                onClick={generatePaymentQR}\n                initial=\"hidden\"\n                animate=\"visible\"\n                variants={popUp}\n                type=\"submit\"\n                className=\"px-4 py-1.5 w-9/12 sm:w-1/2 flex justify-center mx-auto rounded-lg font-semibold bg-black text-white dark:bg-white dark:text-black clickable_button\"\n              >\n                Pay{\" \"}\n                {amount && (\n                  <span className=\"ml-2 line-clamp-1\">&#8377; {amount}</span>\n                )}\n              </motion.button>\n            )}\n          </>\n        ) : (\n          <AnimatedDiv\n            variants={FadeContainer}\n            className=\"flex flex-col items-center\"\n          >\n            <QRCode\n              className=\"mx-auto scale-75\"\n              id=\"QRCode\"\n              value={qrValue}\n              bgColor={isDarkMode ? \"#25282a\" : \"white\"}\n              fgColor={isDarkMode ? \"white\" : \"#25282a\"}\n            />\n\n            <div className=\"flex items-center justify-center gap-2 my-5 text-sm text-gray-600 dark:text-gray-200\">\n              <FiInfo className=\"w-5 h-5\" />\n              <p className=\"text-xs\">Scan the QR code via any UPI app </p>\n            </div>\n          </AnimatedDiv>\n        )}\n      </motion.div>\n    </motion.div>\n  );\n}\n"
  },
  {
    "path": "components/TableOfContents.tsx",
    "content": "import { AnimatePresence, motion } from \"framer-motion\";\nimport { useState } from \"react\";\n\nimport Link from \"next/link\";\nimport { BsListUl } from \"react-icons/bs\";\nimport { MdClose } from \"react-icons/md\";\nimport { CgSearch } from \"react-icons/cg\";\nimport { TableOfContents as TableOfContentType } from \"@lib/types\";\nimport { stringToSlug } from \"@lib/toc\";\n\nexport default function TableOfContents({\n  tableOfContents,\n}: {\n  tableOfContents: TableOfContentType[];\n}) {\n  const [open, setOpen] = useState(false);\n  const [search, setSearch] = useState(\"\");\n\n  if (!tableOfContents.length) return null;\n\n  const filtered = search.trim()\n    ? tableOfContents.filter((t) =>\n        t.heading.toLowerCase().includes(search.trim().toLowerCase()),\n      )\n    : tableOfContents;\n\n  return (\n    <>\n      {/* FAB — flat, no shadow */}\n      <motion.button\n        initial={{ opacity: 0, y: 8 }}\n        animate={{ opacity: 1, y: 0 }}\n        transition={{ delay: 0.6 }}\n        onClick={() => setOpen((o) => !o)}\n        aria-label=\"Toggle Table of Contents\"\n        className=\"fixed bottom-6 left-6 z-40 flex items-center gap-2 px-3 h-9 bg-gray-900 dark:bg-white text-white dark:text-gray-900 font-mono text-[10px] tracking-[0.35em] uppercase print:hidden\"\n      >\n        <BsListUl className=\"w-3.5 h-3.5 flex-shrink-0\" />\n        <span className=\"hidden sm:inline\">Contents</span>\n      </motion.button>\n\n      {/* Backdrop */}\n      <AnimatePresence>\n        {open && (\n          <motion.div\n            key=\"toc-backdrop\"\n            initial={{ opacity: 0 }}\n            animate={{ opacity: 1 }}\n            exit={{ opacity: 0 }}\n            onClick={() => setOpen(false)}\n            className=\"fixed inset-0 z-40 bg-black/40 print:hidden\"\n          />\n        )}\n      </AnimatePresence>\n\n      {/* Right drawer */}\n      <AnimatePresence>\n        {open && (\n          <motion.aside\n            key=\"toc-drawer\"\n            initial={{ x: \"-100%\" }}\n            animate={{ x: 0 }}\n            exit={{ x: \"-100%\" }}\n            style={{ zIndex: 100 }}\n            transition={{ type: \"spring\", stiffness: 340, damping: 32 }}\n            className=\"fixed top-0 left-0 bottom-0 w-full sm:w-80 bg-white dark:bg-darkPrimary border-r border-gray-200 dark:border-neutral-700 flex flex-col print:hidden\"\n          >\n            {/* Top accent bar */}\n            <div className=\"h-0.5 w-full bg-gray-900 dark:bg-white flex-shrink-0\" />\n\n            {/* Drawer header */}\n            <div className=\"flex items-center justify-between px-5 py-4 border-b border-gray-200 dark:border-neutral-700 flex-shrink-0\">\n              <div>\n                <span className=\"font-mono text-[10px] tracking-[0.45em] uppercase text-gray-400 dark:text-gray-500 block leading-none mb-1\">\n                  Navigation\n                </span>\n                <span className=\"text-sm font-semibold text-gray-900 dark:text-white\">\n                  Table of Contents\n                </span>\n              </div>\n              <button\n                onClick={() => setOpen(false)}\n                aria-label=\"Close\"\n                className=\"w-8 h-8 flex items-center justify-center border border-gray-200 dark:border-gray-700 text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:border-gray-900 dark:hover:border-white transition-colors\"\n              >\n                <MdClose className=\"w-4 h-4\" />\n              </button>\n            </div>\n\n            {/* Search */}\n            <div className=\"px-5 py-3 border-b border-gray-200 dark:border-neutral-700 flex-shrink-0\">\n              <div className=\"relative\">\n                <CgSearch className=\"absolute left-3 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-gray-400 pointer-events-none\" />\n                <input\n                  type=\"text\"\n                  value={search}\n                  onChange={(e) => setSearch(e.target.value)}\n                  placeholder=\"Filter sections…\"\n                  className=\"w-full pl-8 pr-3 py-2 text-sm bg-transparent border border-gray-200 dark:border-gray-700 text-gray-900 dark:text-white placeholder:text-gray-400 dark:placeholder:text-gray-500 outline-none focus:border-gray-900 dark:focus:border-white transition-colors\"\n                />\n              </div>\n            </div>\n\n            {/* TOC items */}\n            <nav className=\"flex-1 overflow-y-auto py-4\">\n              {filtered.length === 0 ? (\n                <p className=\"px-5 py-6 font-mono text-[11px] tracking-[0.35em] uppercase text-gray-400 dark:text-gray-500 text-center\">\n                  No results\n                </p>\n              ) : (\n                filtered.map((item) => (\n                  <Link\n                    key={item.id}\n                    href={`#${stringToSlug(item.id)}`}\n                    onClick={() => {\n                      setOpen(false);\n                      setSearch(\"\");\n                    }}\n                    style={{ paddingLeft: `${(item.level - 1) * 14 + 20}px` }}\n                    className={`flex items-start gap-2.5 py-3 pr-6 text-sm border-l-2 transition-all group ${\n                      item.level === 1\n                        ? \"border-transparent hover:border-gray-900 dark:hover:border-white text-gray-900 dark:text-white font-medium hover:bg-gray-50 dark:hover:bg-white/5\"\n                        : \"border-transparent hover:border-gray-400 dark:hover:border-gray-600 text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white hover:bg-gray-50 dark:hover:bg-white/5\"\n                    }`}\n                  >\n                    {item.level > 1 && (\n                      <span className=\"mt-[6px] w-1 h-1 rounded-full bg-current flex-shrink-0 opacity-50 group-hover:opacity-100 transition-opacity\" />\n                    )}\n                    <span className=\"leading-snug\">{item.heading}</span>\n                  </Link>\n                ))\n              )}\n            </nav>\n\n            {/* Footer */}\n            <div className=\"px-5 py-3.5 border-t border-gray-200 dark:border-neutral-700 bg-gray-50 dark:bg-darkSecondary flex-shrink-0 flex items-center justify-between\">\n              <span className=\"font-mono text-[10px] tracking-[0.35em] uppercase text-gray-400 dark:text-gray-500\">\n                {filtered.length} section{filtered.length !== 1 ? \"s\" : \"\"}\n              </span>\n              {search && (\n                <button\n                  onClick={() => setSearch(\"\")}\n                  className=\"font-mono text-[10px] tracking-[0.3em] uppercase text-gray-400 hover:text-gray-900 dark:hover:text-white transition-colors\"\n                >\n                  Clear\n                </button>\n              )}\n            </div>\n          </motion.aside>\n        )}\n      </AnimatePresence>\n    </>\n  );\n}\n"
  },
  {
    "path": "components/TopNavbar.tsx",
    "content": "/* Importing Modules */\nimport React, { useEffect, useState, useRef, useCallback } from \"react\";\nimport Link from \"next/link\";\nimport { useRouter } from \"next/router\";\nimport { motion, useAnimation, AnimatePresence } from \"framer-motion\";\nimport {\n  FadeContainer,\n  hamFastFadeContainer,\n  mobileNavItemSideways,\n  popUp,\n} from \"../content/FramerMotionVariants\";\nimport { useDarkMode } from \"../context/darkModeContext\";\nimport { navigationRoutes } from \"../utils/utils\";\nimport Logo from \"./SVG/Logo\";\nimport { DarkModeSwitch } from \"react-toggle-dark-mode\";\n\n/* TopNavbar Component */\nexport default function TopNavbar() {\n  const navRef = useRef<HTMLDivElement>(null);\n  const control = useAnimation();\n  const [navOpen, setNavOpen] = useState(false);\n  const { isDarkMode, changeDarkMode } = useDarkMode();\n\n  const addShadowToNavbar = useCallback(() => {\n    if (window.pageYOffset > 10) {\n      navRef.current!.classList.add(\n        ...[\n          \"border-b\",\n          \"border-gray-200\",\n          \"dark:border-neutral-700\",\n          \"backdrop-blur-xl\",\n          \"bg-white/80\",\n          \"dark:bg-darkPrimary/90\",\n        ],\n      );\n      control.start(\"visible\");\n    } else {\n      navRef.current!.classList.remove(\n        ...[\n          \"border-b\",\n          \"border-gray-200\",\n          \"dark:border-neutral-700\",\n          \"backdrop-blur-xl\",\n          \"bg-white/80\",\n          \"dark:bg-darkPrimary/90\",\n        ],\n      );\n      control.start(\"hidden\");\n    }\n  }, [control]);\n\n  useEffect(() => {\n    window.addEventListener(\"scroll\", addShadowToNavbar);\n    return () => window.removeEventListener(\"scroll\", addShadowToNavbar);\n  }, [addShadowToNavbar]);\n\n  function lockScroll() {\n    document.getElementsByTagName(\"html\")[0].classList.toggle(\"lock-scroll\");\n  }\n\n  function handleClick() {\n    lockScroll();\n    setNavOpen(!navOpen);\n  }\n\n  return (\n    <>\n      <div\n        className=\"fixed w-full top-0 z-50 print:hidden transition-colors duration-200\"\n        ref={navRef}\n      >\n        <div className=\"max-w-7xl mx-auto px-6 sm:px-8 lg:px-12 flex items-center justify-between h-14\">\n          {/* Logo / name */}\n          <Link\n            href=\"/\"\n            aria-label=\"Home\"\n            className=\"flex items-center gap-2.5 flex-shrink-0\"\n          >\n            <Logo className=\"w-7 h-7 text-black dark:text-white\" />\n            <motion.span\n              initial=\"hidden\"\n              animate={control}\n              variants={{\n                hidden: { opacity: 0, x: -6 },\n                visible: { opacity: 1, x: 0, transition: { duration: 0.2 } },\n              }}\n              className=\"font-sarina text-gray-900 dark:text-white text-sm hidden sm:inline\"\n            >\n              Jatin Sharma\n            </motion.span>\n          </Link>\n\n          {/* Desktop nav */}\n          <motion.nav\n            initial=\"hidden\"\n            animate=\"visible\"\n            variants={FadeContainer}\n            className=\"hidden sm:flex items-center gap-1\"\n          >\n            {navigationRoutes.slice(0, 8).map((link, i) => (\n              <NavItem key={i} href={`/${link}`} text={link} />\n            ))}\n          </motion.nav>\n\n          {/* Right — dark mode + hamburger */}\n          <div className=\"flex items-center gap-4\">\n            <motion.div\n              initial=\"hidden\"\n              animate=\"visible\"\n              variants={popUp}\n              className=\"cursor-pointer\"\n              title=\"Toggle Theme\"\n            >\n              <DarkModeSwitch\n                checked={isDarkMode}\n                onChange={changeDarkMode}\n                size={20}\n              />\n            </motion.div>\n\n            {/* Hamburger — mobile only */}\n            <HamBurger open={navOpen} handleClick={handleClick} />\n          </div>\n        </div>\n      </div>\n\n      {/* Mobile menu — rendered outside navRef to avoid backdrop-filter\n          creating a new fixed containing block that clips the overlay */}\n      <AnimatePresence>\n        {navOpen && (\n          <MobileMenu links={navigationRoutes} handleClick={handleClick} />\n        )}\n      </AnimatePresence>\n    </>\n  );\n}\n\n// NavItem\nfunction NavItem({ href, text }: { href: string; text: string }) {\n  const router = useRouter();\n  const isActive = router.asPath === (href === \"/home\" ? \"/\" : href);\n  return (\n    <Link\n      href={href === \"/home\" ? \"/\" : href}\n      className={`relative px-3 py-1.5 text-sm font-medium capitalize transition-colors rounded group ${\n        isActive\n          ? \"text-gray-900 dark:text-white\"\n          : \"text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white\"\n      }`}\n    >\n      <motion.span variants={popUp}>{text}</motion.span>\n      {/* active underline */}\n      {isActive && (\n        <motion.span\n          layoutId=\"nav-underline\"\n          className=\"absolute bottom-0 left-3 right-3 h-px bg-gray-900 dark:bg-white\"\n        />\n      )}\n    </Link>\n  );\n}\n\n// Hamburger — sm:hidden\nfunction HamBurger({\n  open,\n  handleClick,\n}: {\n  open: boolean;\n  handleClick: () => void;\n}) {\n  return (\n    <motion.button\n      initial=\"hidden\"\n      animate=\"visible\"\n      variants={popUp}\n      onClick={handleClick}\n      aria-label={open ? \"Close menu\" : \"Open menu\"}\n      className=\"sm:hidden flex flex-col justify-center items-center w-8 h-8 gap-[5px] cursor-pointer select-none\"\n    >\n      <motion.span\n        animate={open ? { rotate: 45, y: 7 } : { rotate: 0, y: 0 }}\n        transition={{ duration: 0.2 }}\n        className=\"block w-5 h-px bg-gray-900 dark:bg-white origin-center\"\n      />\n      <motion.span\n        animate={open ? { opacity: 0, scaleX: 0 } : { opacity: 1, scaleX: 1 }}\n        transition={{ duration: 0.15 }}\n        className=\"block w-5 h-px bg-gray-900 dark:bg-white\"\n      />\n      <motion.span\n        animate={open ? { rotate: -45, y: -7 } : { rotate: 0, y: 0 }}\n        transition={{ duration: 0.2 }}\n        className=\"block w-5 h-px bg-gray-900 dark:bg-white origin-center\"\n      />\n    </motion.button>\n  );\n}\n\n// Mobile menu — full-screen\nconst MobileMenu = ({\n  links,\n  handleClick,\n}: {\n  links: string[];\n  handleClick: () => void;\n}) => {\n  return (\n    <motion.div\n      className=\"fixed inset-0 top-14 z-40 bg-white dark:bg-darkPrimary sm:hidden overflow-y-auto\"\n      initial={{ opacity: 0, y: -8 }}\n      animate={{ opacity: 1, y: 0 }}\n      exit={{ opacity: 0, y: -8 }}\n      transition={{ duration: 0.18, ease: \"easeOut\" }}\n    >\n      {/* Watermark */}\n      <div\n        className=\"absolute right-4 top-2 font-black select-none pointer-events-none leading-none tracking-tighter bg-gradient-to-b from-gray-200 to-gray-50 dark:from-[#232628] dark:to-darkPrimary bg-clip-text text-transparent\"\n        style={{ fontSize: \"clamp(4rem, 20vw, 7rem)\" }}\n        aria-hidden=\"true\"\n      >\n        MENU\n      </div>\n\n      <nav className=\"px-6 pt-8 pb-16\">\n        {/* mono label */}\n        <span className=\"font-mono text-[9px] tracking-[0.45em] uppercase text-gray-500 dark:text-gray-500 mb-6 block\">\n          Navigation\n        </span>\n\n        <motion.div\n          className=\"divide-y divide-gray-200 dark:divide-neutral-700\"\n          variants={hamFastFadeContainer}\n          initial=\"hidden\"\n          animate=\"visible\"\n        >\n          {links.slice(0, 8).map((link, index) => {\n            const href =\n              link.toLowerCase() === \"home\" ? \"/\" : `/${link.toLowerCase()}`;\n            const label = link === \"rss\" ? \"RSS\" : link;\n            return (\n              <motion.div\n                key={`mobileNav-${index}`}\n                variants={mobileNavItemSideways}\n              >\n                <Link\n                  href={href}\n                  onClick={handleClick}\n                  className=\"flex items-center justify-between py-4 group\"\n                >\n                  <span className=\"text-2xl font-bold capitalize text-gray-900 dark:text-white group-hover:translate-x-1 transition-transform duration-200\">\n                    {label}\n                  </span>\n                  <span className=\"text-gray-400 dark:text-gray-600 group-hover:text-gray-900 dark:group-hover:text-white font-mono text-sm transition-colors\">\n                    {String(index + 1).padStart(2, \"0\")}\n                  </span>\n                </Link>\n              </motion.div>\n            );\n          })}\n        </motion.div>\n      </nav>\n    </motion.div>\n  );\n};\n"
  },
  {
    "path": "content/FramerMotionVariants.ts",
    "content": "import { Variants } from \"framer-motion\";\n\nexport const popUp: Variants = {\n  hidden: { scale: 0, opacity: 0 },\n  visible: {\n    opacity: 1,\n    scale: 1,\n    transition: {\n      type: \"spring\",\n    },\n  },\n};\n\nexport const popUpFromBottomForText: Variants = {\n  hidden: { opacity: 0, y: 40 },\n  visible: {\n    opacity: 1,\n    y: 0,\n    transition: {\n      type: \"spring\",\n      stiffness: 60,\n    },\n  },\n};\n\nexport const headingFromLeft: Variants = {\n  hidden: { x: -200, opacity: 0 },\n  visible: {\n    x: 0,\n    opacity: 1,\n    transition: {\n      duration: 0.1,\n      type: \"spring\",\n      stiffness: 70,\n    },\n  },\n};\n\nexport const fromLeftVariant: Variants = {\n  hidden: { x: -100, opacity: 0 },\n  visible: {\n    x: 0,\n    opacity: 1,\n    transition: {\n      duration: 0.1,\n      type: \"spring\",\n      stiffness: 100,\n    },\n  },\n};\n\nexport const fromLeftChildren: Variants = {\n  hidden: { x: -100, opacity: 0 },\n  visible: {\n    x: 0,\n    opacity: 1,\n    transition: {\n      duration: 0.1,\n    },\n  },\n};\n\nexport const fromTopVariant: Variants = {\n  hidden: { y: -100, opacity: 0 },\n  visible: {\n    y: 0,\n    opacity: 1,\n    transition: {\n      duration: 0.1,\n      type: \"spring\",\n      stiffness: 100,\n    },\n  },\n};\n\nexport const opacityVariant: Variants = {\n  hidden: { opacity: 0 },\n  visible: { opacity: 1, transition: { delay: 0.2 } },\n};\n\nexport const hamFastFadeContainer: Variants = {\n  hidden: { opacity: 0 },\n  visible: {\n    opacity: 1,\n    transition: {\n      delayChildren: 0,\n      staggerChildren: 0.1,\n    },\n  },\n};\n\nexport const mobileNavItemSideways: Variants = {\n  hidden: { x: -40, opacity: 0 },\n  visible: {\n    x: 0,\n    opacity: 1,\n  },\n};\n\nexport const FadeContainer: Variants = {\n  hidden: { opacity: 0 },\n  visible: {\n    opacity: 1,\n    transition: { delayChildren: 0, staggerChildren: 0.1 },\n  },\n};\n\nexport const svgVariant: Variants = {\n  hidden: {\n    pathLength: 0,\n  },\n  visible: {\n    pathLength: 1,\n    transition: {\n      duration: 4,\n      ease: \"easeInOut\",\n    },\n  },\n};\n\nexport const searchBarSlideAnimation: Variants = {\n  hidden: {\n    width: 0,\n    opacity: 0,\n  },\n  visible: {\n    width: \"100%\",\n    opacity: 1,\n    transition: {\n      type: \"linear\",\n      duration: 1,\n    },\n  },\n};\n\nexport const BlogCardAnimation: Variants = {\n  hidden: {\n    y: 50,\n    opacity: 0,\n  },\n  visible: {\n    y: 0,\n    opacity: 1,\n    transition: {\n      delay: 0.2,\n    },\n  },\n};\n"
  },
  {
    "path": "content/meta.ts",
    "content": "import { PageMeta } from \"@lib/types\";\n\nconst pageMeta: PageMeta = {\n  home: {\n    title: \"\",\n    description:\n      \"Hey, I am Jatin Sharma. A Front-end Developer/React Developer from India who loves to design and code. I use React.js or Next.js to build the web application interfaces and the functionalities. At the moment, I am pursuing my Bachelor's degree in Computer Science.\",\n    image: \"https://imgur.com/KeJgIVl.png\",\n    keywords: \"portfolio jatin, portfolio j471n, jatin blogs\",\n  },\n\n  stats: {\n    title: \"Statistics\",\n    description:\n      \"These are my personal statistics about me. It includes My Blogs and github Stats and top music stats.\",\n    image: \"https://imgur.com/9scFfW5.png\",\n    keywords: \"stats, Statistics\",\n  },\n  utilities: {\n    title: \"Utilities\",\n    description:\n      \"In case you are wondering What tech I use, Here's the list of what tech I'm currently using for coding on the daily basis. This list is always changing.\",\n    image: \"https://imgur.com/MpfymCd.png\",\n    keywords: \"Utilities, what i use?, utils, setup, uses,\",\n  },\n  blogs: {\n    title: \"Blogs\",\n    description:\n      \"I've been writing online since 2021, mostly about web development and tech careers. In total, I've written more than 50 articles till now.\",\n    image: \"https://imgur.com/nbNLLZk.png\",\n    keywords: \"j471n blog, blog, webdev, react\",\n  },\n\n  bookmark: {\n    title: \"Bookmarked Blogs\",\n    description: \"Bookmarked Blogs of Jatin Sharma's blogs by you\",\n    image: \"https://imgur.com/5XkrVPq.png\",\n    keywords: \"bookmark, blogs, \",\n  },\n  certificates: {\n    title: \"Certificates\",\n    description:\n      \"I've participated in many contests, courses and test and get certified in many skills. You can find the certificates below.\",\n    image: \"https://imgur.com/J0q1OdT.png\",\n    keywords: \"Certificates, verified\",\n  },\n  projects: {\n    title: \"Projects\",\n    description:\n      \"I've been making various types of projects some of them were basics and some of them were complicated.\",\n    image: \"https://imgur.com/XJqiuNK.png\",\n    keywords: \"projects, work, side project,\",\n  },\n  about: {\n    title: \"About\",\n    description:\n      \"Hey, I am Jatin Sharma. A Front-end Developer/React Developer from India who loves to design and code. I use React.js or Next.js to build the web application interfaces and the functionalities. At the moment, I am pursuing my Bachelor's degree in Computer Science.\",\n    image: \"https://imgur.com/b0HRaPv.png\",\n    keywords: \"about, about me, \",\n  },\n  privacy: {\n    title: \"Privacy Policy\",\n    description:\n      \"Privacy is an important factor for everyone. Following is the privacy policies of j471n.in\",\n    image: \"https://imgur.com/ghlRutT.png\",\n    keywords: \"Privacy, Privacy Policies, \",\n  },\n  snippets: {\n    title: \"Code Snippets\",\n    description:\n      \"These are a collection of code snippets I've used in the past and saved. These could be useful to you as well.\",\n    image: \"https://imgur.com/KeJgIVl.png\",\n    keywords: \"Code, Code Snippets, Snippets\",\n  },\n  books: {\n    title: \"Books\",\n    description:\n      \"Books I've read, am currently reading, or want to read. My personal bookshelf tracked on Hardcover.\",\n    image: \"https://imgur.com/KeJgIVl.png\",\n    keywords: \"books, reading, bookshelf, hardcover\",\n  },\n  epigraphs: {\n    title: \"Epigraphs\",\n    description:\n      \"A personal collection of quotes, passages, and stanzas from books, movies, people, and more that left a mark on me.\",\n    image: \"https://imgur.com/KeJgIVl.png\",\n    keywords: \"quotes, epigraphs, passages, stanzas, books, movies\",\n  },\n};\n\nexport default pageMeta;\n"
  },
  {
    "path": "content/siteConfig.ts",
    "content": "import { homeProfileImage } from \"@utils/utils\";\n\nexport type SocialIconKey =\n  | \"twitter\"\n  | \"linkedin\"\n  | \"github\"\n  | \"instagram\"\n  | \"devto\"\n  | \"codepen\"\n  | \"facebook\"\n  | \"mail\";\n\nexport type StatIconKey = \"code\" | \"users\" | \"coffee\" | \"award\";\n\nconst siteConfig = {\n  person: {\n    name: \"Jatin Sharma\",\n    profileImage: homeProfileImage,\n    email: \"work.j471n@gmail.com\",\n    location: \"Based in India\",\n    availability: \"Open to new projects\",\n  },\n\n  socialLinks: [\n    {\n      title: \"Twitter\",\n      icon: \"twitter\" as SocialIconKey,\n      url: \"https://twitter.com/intent/follow?screen_name=j471n_\",\n      featured: true,\n    },\n    {\n      title: \"LinkedIn\",\n      icon: \"linkedin\" as SocialIconKey,\n      url: \"https://www.linkedin.com/in/j471n/\",\n      featured: true,\n    },\n    {\n      title: \"Github\",\n      icon: \"github\" as SocialIconKey,\n      url: \"https://github.com/j471n\",\n      featured: true,\n    },\n    {\n      title: \"Instagram\",\n      icon: \"instagram\" as SocialIconKey,\n      url: \"https://www.instagram.com/j471n_\",\n      featured: false,\n    },\n    {\n      title: \"Dev.to\",\n      icon: \"devto\" as SocialIconKey,\n      url: \"https://dev.to/j471n\",\n      featured: false,\n    },\n    {\n      title: \"Codepen\",\n      icon: \"codepen\" as SocialIconKey,\n      url: \"https://codepen.io/j471n\",\n      featured: false,\n    },\n    {\n      title: \"Facebook\",\n      icon: \"facebook\" as SocialIconKey,\n      url: \"https://www.facebook.com/ja7in/\",\n      featured: false,\n    },\n    {\n      title: \"Mail\",\n      icon: \"mail\" as SocialIconKey,\n      url: \"mailto:work.j471n@gmail.com\",\n      featured: false,\n    },\n  ],\n\n  home: {\n    hero: {\n      availabilityBadge: \"Building Something Amazing\",\n      greeting: \"Hi, I'm\",\n      nameHighlight: \"Jatin Sharma\",\n      rolePrefix: \"Tech Lead at\",\n      companyName: \"KonnectNXT\",\n      companyUrl: \"https://www.linkedin.com/company/konnectnxt/\",\n      roleSuffix: \". Turning dreams into reality with modern technologies.\",\n      primaryCta: {\n        label: \"Download Resume\",\n        url: \"https://bit.ly/j471nCV\",\n      },\n      secondaryCta: {\n        label: \"Get in Touch\",\n        url: \"#contact\",\n      },\n      socialLabel: \"Connect:\",\n      experienceBadge: {\n        value: \"3+\",\n        title: \"Years\",\n        description: \"Industry Experience\",\n      },\n    },\n\n    stats: [\n      {\n        icon: \"code\" as StatIconKey,\n        value: \"50+\",\n        label: \"Projects Completed\",\n        description: \"Successful deliveries\",\n      },\n      {\n        icon: \"users\" as StatIconKey,\n        value: \"20+\",\n        label: \"Happy Clients\",\n        description: \"Worldwide satisfaction\",\n      },\n      {\n        icon: \"coffee\" as StatIconKey,\n        value: \"1000+\",\n        label: \"Cups of Coffee\",\n        description: \"Fueling innovation\",\n      },\n      {\n        icon: \"award\" as StatIconKey,\n        value: \"5+\",\n        label: \"Years Experience\",\n        description: \"In web development\",\n      },\n    ],\n\n    skillsSection: {\n      eyebrow: \"Technical Expertise\",\n      title: \"Skills & Technologies\",\n      description:\n        \"A comprehensive toolkit of modern technologies I work with to build exceptional digital experiences\",\n    },\n\n    blogsSection: {\n      eyebrow: \"Latest Writing\",\n      title: \"Featured Articles\",\n      description:\n        \"Thoughts on web development, technology trends, and software engineering best practices\",\n      ctaLabel: \"View All\",\n    },\n\n    epigraphsSection: {\n      eyebrow: \"Collected Thoughts\",\n      title: \"Epigraphs\",\n      description:\n        \"Quotes, passages, and stanzas from books, movies, and life that left a mark\",\n      ctaLabel: \"View All\",\n    },\n  },\n\n  contact: {\n    eyebrow: \"Let's Work Together\",\n    title: \"Get in Touch\",\n    description:\n      \"Have a project in mind or just want to chat? I'm always open to discussing new opportunities, creative ideas, or partnerships.\",\n    email: {\n      title: \"Email\",\n      responseTime: \"Response within 24 hours\",\n    },\n    location: {\n      title: \"Location\",\n      value: \"Based in India\",\n    },\n    availability: {\n      title: \"Availability\",\n      value: \"Open to new projects\",\n    },\n    socialTitle: \"Connect\",\n    servicesTitle: \"Services\",\n    services: [\n      \"Web Development\",\n      \"Technical Consulting\",\n      \"Code Review\",\n      \"Mentorship\",\n    ],\n    privacyNote:\n      \"Your information is safe and will never be shared with third parties.\",\n  },\n\n  footer: {\n    description:\n      \"Full-stack developer passionate about creating beautiful and functional web experiences.\",\n  },\n} as const;\n\nexport const featuredSocialLinks = siteConfig.socialLinks.filter(\n  (socialLink) => socialLink.featured,\n);\n\nexport default siteConfig;\n"
  },
  {
    "path": "content/skillsData.ts",
    "content": "import { SkillType } from \"@lib/types\";\nimport {\n  SiHtml5,\n  SiCss3,\n  SiJavascript,\n  SiNextdotjs,\n  SiTailwindcss,\n  SiPython,\n  SiGit,\n  SiMysql,\n  SiFirebase,\n  SiTypescript,\n  SiReact,\n  SiC,\n  SiCplusplus,\n  SiFigma,\n  SiSupabase,\n  SiNodedotjs,\n  SiDjango,\n  SiRedux,\n  SiDocker,\n  SiVercel,\n} from \"react-icons/si\";\n\nconst skills: SkillType[] = [\n  {\n    name: \"HTML\",\n    Icon: SiHtml5,\n    category: \"Frontend\",\n  },\n  {\n    name: \"CSS\",\n    Icon: SiCss3,\n    category: \"Frontend\",\n  },\n  {\n    name: \"Javascript\",\n    Icon: SiJavascript,\n    category: \"Frontend\",\n  },\n  {\n    name: \"Typescript\",\n    Icon: SiTypescript,\n    category: \"Frontend\",\n  },\n  {\n    name: \"React.js\",\n    Icon: SiReact,\n    category: \"Frontend\",\n  },\n  {\n    name: \"Next.js\",\n    Icon: SiNextdotjs,\n    category: \"Frontend\",\n  },\n  {\n    name: \"Tailwind CSS\",\n    Icon: SiTailwindcss,\n    category: \"Frontend\",\n  },\n  {\n    name: \"Node.js\",\n    Icon: SiNodedotjs,\n    category: \"Backend\",\n  },\n  {\n    name: \"Python\",\n    Icon: SiPython,\n    category: \"Backend\",\n  },\n  {\n    name: \"Django\",\n    Icon: SiDjango,\n    category: \"Backend\",\n  },\n  {\n    name: \"C Programming\",\n    Icon: SiC,\n    category: \"Programming\",\n  },\n  {\n    name: \"C++\",\n    Icon: SiCplusplus,\n    category: \"Programming\",\n  },\n  {\n    name: \"MySQL\",\n    Icon: SiMysql,\n    category: \"Database\",\n  },\n  {\n    name: \"Firebase\",\n    Icon: SiFirebase,\n    category: \"Database\",\n  },\n  {\n    name: \"Supabase\",\n    Icon: SiSupabase,\n    category: \"Database\",\n  },\n  {\n    name: \"Git\",\n    Icon: SiGit,\n    category: \"Tools\",\n  },\n  {\n    name: \"Figma\",\n    Icon: SiFigma,\n    category: \"Tools\",\n  },\n  {\n    name: \"Redux\",\n    Icon: SiRedux,\n    category: \"Frontend\",\n  },\n  {\n    name: \"Docker\",\n    Icon: SiDocker,\n    category: \"Tools\",\n  },\n  {\n    name: \"Vercel\",\n    Icon: SiVercel,\n    category: \"Tools\",\n  },\n];\n\nexport default skills;\n"
  },
  {
    "path": "content/socialMedia.ts",
    "content": "import { SocialPlatform } from \"@lib/types\";\nimport { AiOutlineInstagram, AiOutlineTwitter } from \"react-icons/ai\";\nimport { BsFacebook, BsGithub, BsLinkedin } from \"react-icons/bs\";\nimport { FaDev } from \"react-icons/fa\";\nimport { HiMail } from \"react-icons/hi\";\nimport { SiCodepen } from \"react-icons/si\";\nimport siteConfig, { SocialIconKey } from \"./siteConfig\";\n\nconst iconMap: Record<SocialIconKey, SocialPlatform[\"Icon\"]> = {\n  twitter: AiOutlineTwitter,\n  linkedin: BsLinkedin,\n  github: BsGithub,\n  instagram: AiOutlineInstagram,\n  devto: FaDev,\n  codepen: SiCodepen,\n  facebook: BsFacebook,\n  mail: HiMail,\n};\n\nconst socialMedia: SocialPlatform[] = siteConfig.socialLinks.map(\n  (socialLink) => ({\n    title: socialLink.title,\n    Icon: iconMap[socialLink.icon],\n    url: socialLink.url,\n  }),\n);\n\nexport default socialMedia;\n"
  },
  {
    "path": "content/support.ts",
    "content": "import { SiBuymeacoffee } from \"react-icons/si\";\nimport { BsPaypal } from \"react-icons/bs\";\nimport { SupportMe } from \"@lib/types\";\n\nconst supportOptions: SupportMe[] = [\n  {\n    name: \"Buy Me a Coffee\",\n    url: \"https://buymeacoffee.com/j471n\",\n    Icon: SiBuymeacoffee,\n  },\n  {\n    name: \"PayPal\",\n    url: \"https://paypal.me/j47in\",\n    Icon: BsPaypal,\n  },\n];\n\nexport default supportOptions;\n"
  },
  {
    "path": "content/user.ts",
    "content": "import siteConfig from \"./siteConfig\";\n\ntype AuthorInfo = {\n  name: string;\n  image: string;\n  org: string | null;\n  org_logo: string | null;\n  org_url: string | null;\n};\n\nexport function getAuthorData(org: string | null = null): AuthorInfo {\n  switch (org) {\n    case \"documatic\":\n      return {\n        name: siteConfig.person.name,\n        image: siteConfig.person.profileImage,\n        org: \"Documatic\",\n        org_logo: \"https://i.imgur.com/ZqBFtg1.png\",\n        org_url: \"https://www.documatic.com/\",\n      };\n\n    default:\n      return {\n        name: siteConfig.person.name,\n        image: siteConfig.person.profileImage,\n        org: null,\n        org_logo: null,\n        org_url: null,\n      };\n  }\n}\n"
  },
  {
    "path": "content/utilitiesData.ts",
    "content": "import {\n  SiMacos,\n  SiHomebrew,\n  SiIterm2,\n  SiWarp,\n  SiArc,\n  SiGooglechrome,\n  SiVisualstudiocode,\n  SiXcode,\n  SiDocker,\n  SiInsomnia,\n  SiPostman,\n  SiPrettier,\n  SiPnpm,\n  SiBun,\n  SiNodedotjs,\n  SiFigma,\n  SiCanva,\n  SiObsstudio,\n  SiNotion,\n  SiObsidian,\n  SiLinear,\n  SiGrammarly,\n  SiSpotify,\n  SiBitwarden,\n  SiVercel,\n  SiNetlify,\n  SiSupabase,\n  SiSlack,\n  SiDiscord,\n  SiZoom,\n  SiTelegram,\n  SiGithub,\n  SiTailwindcss,\n  SiTypescript,\n} from \"react-icons/si\";\nimport {\n  BsFillTerminalFill,\n  BsApple,\n  BsGithub,\n  BsDatabase,\n  BsCommand,\n  BsWindowStack,\n} from \"react-icons/bs\";\nimport { FaGitAlt } from \"react-icons/fa\";\nimport SVG from \"@components/SVG\";\nimport { Utilities } from \"@lib/types\";\n\nconst utilities: Utilities = {\n  title: \"Utilities\",\n  description:\n    \"The tools, apps, and services I use daily for development, design, and productivity on macOS. This list is always evolving.\",\n  lastUpdate: \"April 11, 2026\",\n  data: [\n    {\n      title: \"System & OS\",\n      data: [\n        {\n          name: \"macOS\",\n          description: \"Primary operating system\",\n          Icon: SiMacos,\n          link: \"https://www.apple.com/macos/\",\n        },\n        {\n          name: \"Homebrew\",\n          description: \"Package manager for macOS\",\n          Icon: SiHomebrew,\n          link: \"https://brew.sh/\",\n        },\n        {\n          name: \"Raycast\",\n          description: \"Spotlight replacement & launcher\",\n          Icon: BsCommand,\n          link: \"https://www.raycast.com/\",\n        },\n        {\n          name: \"Rectangle\",\n          description: \"Window management with keyboard shortcuts\",\n          Icon: BsWindowStack,\n          link: \"https://rectangleapp.com/\",\n        },\n        {\n          name: \"Bitwarden\",\n          description: \"Open-source password manager\",\n          Icon: SiBitwarden,\n          link: \"https://bitwarden.com/\",\n        },\n        {\n          name: \"Raindrop.io\",\n          description: \"Bookmark & read-later manager\",\n          Icon: SVG.RainDrop,\n          link: \"https://raindrop.io/\",\n        },\n      ],\n    },\n\n    {\n      title: \"Terminal & CLI\",\n      data: [\n        {\n          name: \"iTerm2\",\n          description: \"Feature-rich terminal emulator\",\n          Icon: SiIterm2,\n          link: \"https://iterm2.com/\",\n        },\n        {\n          name: \"Warp\",\n          description: \"AI-powered modern terminal\",\n          Icon: SiWarp,\n          link: \"https://www.warp.dev/\",\n        },\n        {\n          name: \"Oh My Zsh\",\n          description: \"Zsh configuration framework\",\n          Icon: BsFillTerminalFill,\n          link: \"https://ohmyz.sh/\",\n        },\n        {\n          name: \"Homebrew\",\n          description: \"Package manager for macOS & Linux\",\n          Icon: SiHomebrew,\n          link: \"https://brew.sh/\",\n        },\n        {\n          name: \"Git\",\n          description: \"Version control system\",\n          Icon: FaGitAlt,\n          link: \"https://git-scm.com/\",\n        },\n        {\n          name: \"GitHub CLI\",\n          description: \"GitHub from the command line\",\n          Icon: BsGithub,\n          link: \"https://cli.github.com/\",\n        },\n      ],\n    },\n\n    {\n      title: \"Development\",\n      data: [\n        {\n          name: \"VSCode\",\n          description: \"Primary code editor\",\n          Icon: SiVisualstudiocode,\n          link: \"https://code.visualstudio.com/\",\n        },\n        {\n          name: \"Xcode\",\n          description: \"Apple's IDE for native development\",\n          Icon: SiXcode,\n          link: \"https://developer.apple.com/xcode/\",\n        },\n        {\n          name: \"Docker\",\n          description: \"Containerisation platform\",\n          Icon: SiDocker,\n          link: \"https://www.docker.com/products/docker-desktop/\",\n        },\n        {\n          name: \"TablePlus\",\n          description: \"GUI for relational databases\",\n          Icon: BsDatabase,\n          link: \"https://tableplus.com/\",\n        },\n        {\n          name: \"Insomnia\",\n          description: \"REST & GraphQL API client\",\n          Icon: SiInsomnia,\n          link: \"https://insomnia.rest/\",\n        },\n        {\n          name: \"Postman\",\n          description: \"API platform for building & testing\",\n          Icon: SiPostman,\n          link: \"https://www.postman.com/\",\n        },\n        {\n          name: \"GitHub Desktop\",\n          description: \"Visual Git client\",\n          Icon: SiGithub,\n          link: \"https://desktop.github.com/\",\n        },\n        {\n          name: \"Prettier\",\n          description: \"Opinionated code formatter\",\n          Icon: SiPrettier,\n          link: \"https://prettier.io/\",\n        },\n        {\n          name: \"pnpm\",\n          description: \"Fast, disk-efficient package manager\",\n          Icon: SiPnpm,\n          link: \"https://pnpm.io/\",\n        },\n        {\n          name: \"Bun\",\n          description: \"All-in-one JS runtime & package manager\",\n          Icon: SiBun,\n          link: \"https://bun.sh/\",\n        },\n        {\n          name: \"Node.js\",\n          description: \"JavaScript runtime environment\",\n          Icon: SiNodedotjs,\n          link: \"https://nodejs.org/\",\n        },\n        {\n          name: \"TypeScript\",\n          description: \"Typed superset of JavaScript\",\n          Icon: SiTypescript,\n          link: \"https://www.typescriptlang.org/\",\n        },\n        {\n          name: \"Tailwind CSS\",\n          description: \"Utility-first CSS framework\",\n          Icon: SiTailwindcss,\n          link: \"https://tailwindcss.com/\",\n        },\n        {\n          name: \"Supabase\",\n          description: \"Open-source Firebase alternative\",\n          Icon: SiSupabase,\n          link: \"https://supabase.com/\",\n        },\n        {\n          name: \"Vercel\",\n          description: \"Frontend cloud deployment platform\",\n          Icon: SiVercel,\n          link: \"https://vercel.com/\",\n        },\n        {\n          name: \"Netlify\",\n          description: \"Web hosting & serverless platform\",\n          Icon: SiNetlify,\n          link: \"https://www.netlify.com/\",\n        },\n      ],\n    },\n\n    {\n      title: \"Design & Creativity\",\n      data: [\n        {\n          name: \"Figma\",\n          description: \"Collaborative UI design tool\",\n          Icon: SiFigma,\n          link: \"https://www.figma.com/\",\n        },\n        {\n          name: \"Canva\",\n          description: \"Quick graphics & social media design\",\n          Icon: SiCanva,\n          link: \"https://www.canva.com/\",\n        },\n        {\n          name: \"OBS Studio\",\n          description: \"Screen recording & live streaming\",\n          Icon: SiObsstudio,\n          link: \"https://obsproject.com/\",\n        },\n        {\n          name: \"CleanShot X\",\n          description: \"Screenshot & screen recording for Mac\",\n          Icon: BsApple,\n          link: \"https://cleanshot.com/\",\n        },\n      ],\n    },\n\n    {\n      title: \"Productivity\",\n      data: [\n        {\n          name: \"Notion\",\n          description: \"All-in-one workspace for notes & docs\",\n          Icon: SiNotion,\n          link: \"https://www.notion.so/\",\n        },\n        {\n          name: \"Obsidian\",\n          description: \"Local-first Markdown knowledge base\",\n          Icon: SiObsidian,\n          link: \"https://obsidian.md/\",\n        },\n        {\n          name: \"Linear\",\n          description: \"Issue tracking & project management\",\n          Icon: SiLinear,\n          link: \"https://linear.app/\",\n        },\n        {\n          name: \"Grammarly\",\n          description: \"AI writing assistant & grammar checker\",\n          Icon: SiGrammarly,\n          link: \"https://www.grammarly.com/\",\n        },\n        {\n          name: \"Spotify\",\n          description: \"Music & podcast streaming\",\n          Icon: SiSpotify,\n          link: \"https://www.spotify.com/\",\n        },\n        {\n          name: \"Keka\",\n          description: \"macOS file archiver\",\n          Icon: BsApple,\n          link: \"https://www.keka.io/\",\n        },\n        {\n          name: \"f.lux\",\n          description: \"Blue light filter & display warmth\",\n          Icon: SVG.Flux,\n          link: \"https://justgetflux.com/\",\n        },\n      ],\n    },\n\n    {\n      title: \"Browsers\",\n      data: [\n        {\n          name: \"Arc\",\n          description: \"Primary browser with workspace tabs\",\n          Icon: SiArc,\n          link: \"https://arc.net/\",\n        },\n        {\n          name: \"Chrome\",\n          description: \"Secondary browser for testing\",\n          Icon: SiGooglechrome,\n          link: \"https://www.google.com/chrome/\",\n        },\n      ],\n    },\n\n    {\n      title: \"Communication\",\n      data: [\n        {\n          name: \"Slack\",\n          description: \"Team messaging & collaboration\",\n          Icon: SiSlack,\n          link: \"https://slack.com/\",\n        },\n        {\n          name: \"Discord\",\n          description: \"Developer communities & friends\",\n          Icon: SiDiscord,\n          link: \"https://discord.com/\",\n        },\n        {\n          name: \"Zoom\",\n          description: \"Video meetings\",\n          Icon: SiZoom,\n          link: \"https://zoom.us/\",\n        },\n        {\n          name: \"Telegram\",\n          description: \"Fast & private messaging\",\n          Icon: SiTelegram,\n          link: \"https://telegram.org/\",\n        },\n      ],\n    },\n  ],\n};\n\nexport default utilities;\n"
  },
  {
    "path": "context/darkModeContext.tsx",
    "content": "import React, { useState, useContext, useEffect, createContext } from \"react\";\n\nexport interface DarkModeContextType {\n  isDarkMode: boolean;\n  changeDarkMode(value: boolean): void;\n}\n\nconst DarkModeContext = createContext<DarkModeContextType | null>(null);\n\nexport function DarkModeProvider({ children }: { children: React.ReactNode }) {\n  const [isDarkMode, setDarkMode] = useState(false);\n\n  function updateTheme() {\n    const currentTheme = localStorage.getItem(\"isDarkMode\") || \"false\";\n    if (currentTheme === \"true\") {\n      document.body.classList.add(\"dark\");\n      setDarkMode(true);\n    } else {\n      document.body.classList.remove(\"dark\");\n      setDarkMode(false);\n    }\n  }\n  useEffect(() => {\n    updateTheme();\n  }, []);\n  function changeDarkMode(value: boolean) {\n    localStorage.setItem(\"isDarkMode\", value.toString());\n    updateTheme();\n  }\n\n  const contextValue: DarkModeContextType = {\n    isDarkMode,\n    changeDarkMode,\n  };\n\n  return (\n    <DarkModeContext.Provider value={contextValue}>\n      {children}\n    </DarkModeContext.Provider>\n  );\n}\n\nexport const useDarkMode = (): DarkModeContextType => {\n  const context = useContext(DarkModeContext);\n  return context!;\n};\n"
  },
  {
    "path": "docs/sanity-deploy.md",
    "content": "# Deploying Sanity Studio\n\nStudio is live at **https://j471n-blog.sanity.studio/**\n\n---\n\n## First-time setup\n\nInstall the Sanity CLI globally (only needed once):\n\n```bash\nnpm i @sanity/cli -g\n```\n\nLog in with your Google account:\n\n```bash\nsanity login\n```\n\n---\n\n## Deploy\n\nRun this from the `sanity/` folder whenever you make schema changes:\n\n```bash\ncd sanity\nsanity deploy\n```\n\n---\n\n## Local dev\n\n```bash\ncd sanity\nsanity dev\n```\n\nOpens the studio at `http://localhost:3333`.\n\n---\n\n## Notes\n\n- **Schema changes** (adding/editing fields) → `sanity deploy`\n- **Content changes** (adding/editing entries) → just hit **Publish** in the studio, no deploy needed\n- **Site updates automatically** — Next.js picks up new content within 5 minutes (ISR)\n"
  },
  {
    "path": "hooks/useBookmarkBlogs.ts",
    "content": "import { useEffect, useState } from \"react\";\n\nimport { BlogPost } from \"@lib/interface/sanity\";\nimport { FrontMatter } from \"@lib/types\";\n\nconst useBookmarkBlogs = (key: string, defaultValue: []) => {\n  const [bookmarkedBlogs, setBookmarkedBlogs] = useState((): BlogPost[] => {\n    let currentValue: BlogPost[] = [];\n\n    try {\n      currentValue = JSON.parse(localStorage.getItem(key)!);\n    } catch (error) {\n      currentValue = defaultValue;\n    }\n\n    return currentValue;\n  });\n\n  function getValue() {\n    var data = JSON.parse(localStorage.getItem(key)!);\n    if (data === null) {\n      localStorage.setItem(key, JSON.stringify([]));\n      return JSON.parse(localStorage.getItem(key)!);\n    }\n    return data;\n  }\n\n  function addToBookmark(blogToBookmark: FrontMatter) {\n    var data = getValue();\n    if (!data.includes(blogToBookmark)) {\n      data.unshift(blogToBookmark); // add blog to the starting of the array\n      setBookmarkedBlogs(data);\n    }\n  }\n\n  function removeFromBookmark(blogToRemove: string) {\n    var data = getValue();\n    setBookmarkedBlogs(\n      data.filter((blog: FrontMatter) => blog.slug != blogToRemove)\n    );\n  }\n\n  function isAlreadyBookmarked(searchBySlug: string) {\n    return bookmarkedBlogs\n      ?.map(\n        (bookmarkedBlog: BlogPost) =>\n          bookmarkedBlog.slug.current === searchBySlug\n      )\n      .includes(true);\n  }\n\n  useEffect(() => {\n    localStorage.setItem(key, JSON.stringify(bookmarkedBlogs));\n  }, [bookmarkedBlogs, key]);\n\n  return {\n    bookmarkedBlogs,\n    addToBookmark,\n    removeFromBookmark,\n    isAlreadyBookmarked,\n  };\n};\n\nexport default useBookmarkBlogs;\n"
  },
  {
    "path": "hooks/useDebounce.ts",
    "content": "import { useState, useEffect } from \"react\";\n\nexport function useDebounce<T>(value: T, delay = 300): T {\n  const [debouncedValue, setDebouncedValue] = useState<T>(value);\n\n  useEffect(() => {\n    const timer = setTimeout(() => setDebouncedValue(value), delay);\n    return () => clearTimeout(timer);\n  }, [value, delay]);\n\n  return debouncedValue;\n}\n"
  },
  {
    "path": "hooks/useFetchWithSWR.ts",
    "content": "import useSWR from \"swr\";\n\n// fetcher to fetch the data\n// const fetcher = (...args) => fetch(...args).then((res) => res.json());\nimport fetcher from \"@lib/fetcher\";\n\nexport default function useFetchWithSWR(url: string) {\n  const { data, error } = useSWR(url, fetcher);\n\n  return {\n    data,\n    isLoading: !error && !data,\n    isError: error,\n  } as {\n    data: any;\n    isLoading: boolean;\n    isError: any;\n  };\n}\n"
  },
  {
    "path": "hooks/useScrollPercentage.ts",
    "content": "import { useEffect, useState, useCallback } from \"react\";\nexport default function useScrollPercentage() {\n  // fifteen\n  const [scrollPercentage, setScrollPercentage] = useState(0);\n  function getScrollPercent(): number {\n    var h = document.documentElement;\n    var b = document.body;\n\n    return (\n      ((h.scrollTop || b.scrollTop) /\n        ((h.scrollHeight || b.scrollHeight) - h.clientHeight)) *\n      100\n    );\n  }\n\n  const scrollEvent = useCallback(() => {\n    setScrollPercentage(getScrollPercent());\n  }, []);\n\n  useEffect(() => {\n    window.addEventListener(\"scroll\", scrollEvent);\n    return () => {\n      window.removeEventListener(\"scroll\", scrollEvent);\n    };\n  }, [scrollEvent]);\n\n  return scrollPercentage;\n}\n"
  },
  {
    "path": "hooks/useShare.ts",
    "content": "import { useEffect, useState } from \"react\";\nfunction useShare() {\n  // state for share supports\n  const [isShareSupported, setIsShareSupported] = useState(false);\n\n  // checking if that exist or not\n  useEffect(() => {\n    setIsShareSupported(() => (\"share\" in navigator ? true : false));\n  }, []);\n\n  return { isShareSupported };\n}\n\nexport default useShare;\n"
  },
  {
    "path": "hooks/useWindowLocation.ts",
    "content": "import { useEffect, useState } from \"react\";\nimport { useRouter } from \"next/router\";\n\ntype URL = string;\n\nexport default function useWindowLocation() {\n  const [currentURL, setCurrentURL] = useState<URL>(\"\");\n  const router = useRouter();\n\n  useEffect(() => {\n    setCurrentURL(window.location.href);\n  }, [router.asPath]);\n\n  return { currentURL };\n}\n"
  },
  {
    "path": "hooks/useWindowSize.ts",
    "content": "import { useState, useEffect } from \"react\";\nexport default function useWindowSize() {\n  // Initialize state with undefined width/height so server and client renders match\n  const [windowSize, setWindowSize] = useState({\n    width: 0,\n    height: 0,\n  });\n  useEffect(() => {\n    // Handler to call on window resize\n    function handleResize() {\n      // Set window width/height to state\n      setWindowSize({\n        width: window.innerWidth,\n        height: window.innerHeight,\n      });\n    }\n    // Add event listener\n    window.addEventListener(\"resize\", handleResize);\n    // Call handler right away so state gets updated with initial window size\n    handleResize();\n    // Remove event listener on cleanup\n    return () => window.removeEventListener(\"resize\", handleResize);\n  }, []); // Empty array ensures that effect is only run on mount\n  return windowSize;\n}\n"
  },
  {
    "path": "layout/BlogLayout.tsx",
    "content": "import { BlogPost } from \"@lib/interface/sanity\";\nimport { FiPrinter } from \"react-icons/fi\";\nimport Image from \"next/image\";\nimport Link from \"next/link\";\nimport ScrollProgressBar from \"@components/ScrollProgressBar\";\nimport TableOfContents from \"@components/TableOfContents\";\nimport { getFormattedDate } from \"@utils/date\";\nimport { motion } from \"framer-motion\";\n\nexport default function BlogLayout({\n  post,\n  children,\n}: {\n  post: BlogPost;\n  children: JSX.Element | string;\n}) {\n  return (\n    <div className=\"relative mt-[44px] md:mt-[60px]\">\n      <ScrollProgressBar />\n      <TableOfContents tableOfContents={post.tableOfContents} />\n\n      {/* Page wrapper */}\n      <div className=\"max-w-3xl mx-auto px-5 sm:px-8 xl:px-0 pb-20\">\n        {/* Article header */}\n        <header className=\"pt-10 pb-8 border-b border-gray-200 dark:border-neutral-700 space-y-6\">\n          {/* Tags / org badge */}\n          {post.organization && (\n            <motion.div\n              initial={{ opacity: 0, y: 8 }}\n              animate={{ opacity: 1, y: 0 }}\n              className=\"flex items-center gap-2\"\n            >\n              <div className=\"flex items-center gap-2 border border-gray-200 dark:border-neutral-700 px-2.5 py-1\">\n                <div className=\"relative w-4 h-4 overflow-hidden\">\n                  <Image\n                    fill\n                    alt={post.organization.name}\n                    src={post.organization.image.asset.url}\n                    className=\"object-contain\"\n                  />\n                </div>\n                <Link\n                  href={post.organization.website}\n                  className=\"font-mono text-[10px] tracking-[0.35em] uppercase text-gray-500 dark:text-gray-500 hover:text-gray-900 dark:hover:text-white transition-colors\"\n                >\n                  {post.organization.name}\n                </Link>\n              </div>\n            </motion.div>\n          )}\n\n          <motion.h1\n            initial={{ opacity: 0, y: 16 }}\n            animate={{ opacity: 1, y: 0 }}\n            transition={{ duration: 0.45, delay: 0.04 }}\n            className=\"text-4xl sm:text-5xl font-bold tracking-tight text-gray-900 dark:text-white leading-tight\"\n          >\n            {post.title}\n          </motion.h1>\n\n          <motion.p\n            initial={{ opacity: 0, y: 10 }}\n            animate={{ opacity: 1, y: 0 }}\n            transition={{ duration: 0.45, delay: 0.1 }}\n            className=\"text-lg text-gray-600 dark:text-gray-400 leading-relaxed border-l-2 border-gray-300 dark:border-gray-700 pl-4\"\n          >\n            {post.excerpt}\n          </motion.p>\n\n          {/* Meta */}\n          <motion.div\n            initial={{ opacity: 0 }}\n            animate={{ opacity: 1 }}\n            transition={{ delay: 0.16 }}\n            className=\"flex flex-wrap items-center justify-between gap-4\"\n          >\n            {/* Author */}\n            <div className=\"flex items-center gap-3\">\n              <div className=\"relative w-8 h-8 rounded-full overflow-hidden flex-shrink-0 border border-gray-200 dark:border-gray-700\">\n                <Image\n                  fill\n                  alt={post.author.name}\n                  src={post.author.image.asset.url}\n                  className=\"object-cover\"\n                />\n              </div>\n              <div>\n                <Link\n                  href=\"/about\"\n                  className=\"text-sm font-semibold text-gray-900 dark:text-white hover:underline\"\n                >\n                  {post.author.name}\n                </Link>\n                <p className=\"font-mono text-[10px] tracking-[0.3em] uppercase text-gray-500 dark:text-gray-500\">\n                  {getFormattedDate(new Date(post.publishedAt))}\n                </p>\n              </div>\n            </div>\n\n            {/* Reading stats + print */}\n            <div className=\"flex items-center gap-3\">\n              <span className=\"font-mono text-[10px] tracking-[0.3em] uppercase text-gray-500 dark:text-gray-500\">\n                {post.readingTime.text}\n              </span>\n              <span className=\"text-gray-300 dark:text-gray-700\">·</span>\n              <span className=\"font-mono text-[10px] tracking-[0.3em] uppercase text-gray-500 dark:text-gray-500\">\n                {post.readingTime.words} words\n              </span>\n              <button\n                title=\"Print\"\n                onClick={() => window.print()}\n                className=\"ml-1 w-7 h-7 flex items-center justify-center border border-gray-200 dark:border-gray-700 text-gray-400 hover:text-gray-900 dark:hover:text-white hover:border-gray-400 dark:hover:border-gray-500 transition-colors print:hidden\"\n              >\n                <FiPrinter className=\"w-3 h-3\" />\n              </button>\n            </div>\n          </motion.div>\n        </header>\n\n        {/* MDX body */}\n        <div className=\"mt-10 max-w-full font-barlow prose-typography\">\n          {children}\n        </div>\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "layout/Layout.tsx",
    "content": "import React, { useState } from \"react\";\n\nimport Footer from \"../components/Footer\";\nimport QRCodeContainer from \"@components/QRCodeContainer\";\nimport ScrollToTopButton from \"../components/ScrollToTopButton\";\nimport SnowfallCanvas from \"@components/SnowfallCanvas\";\nimport TopNavbar from \"../components/TopNavbar\";\nimport { useDarkMode } from \"@context/darkModeContext\";\n\nexport default function Layout({ children }: { children: React.ReactNode }) {\n  const { isDarkMode } = useDarkMode();\n  const [showQR, setShowQR] = useState(false);\n\n  return (\n    <>\n      {(new Date().getMonth() >= 11 || new Date().getMonth() <= 1) &&\n        isDarkMode && <SnowfallCanvas />}\n\n      <TopNavbar />\n      <main>{children}</main>\n      <Footer setShowQR={setShowQR} showQR={showQR} />\n      <ScrollToTopButton />\n      <QRCodeContainer showQR={showQR} setShowQR={setShowQR} />\n    </>\n  );\n}\n"
  },
  {
    "path": "layout/SnippetLayout.tsx",
    "content": "import { ISnippet } from \"@lib/interface/sanity\";\nimport Image from \"next/image\";\nimport { getFormattedDate } from \"@utils/date\";\nimport { motion } from \"framer-motion\";\nimport ScrollProgressBar from \"@components/ScrollProgressBar\";\n\nexport default function SnippetLayout({\n  snippet,\n  children,\n}: {\n  snippet: ISnippet;\n  children: JSX.Element;\n}) {\n  return (\n    <div className=\"relative mt-[44px] md:mt-[60px]\">\n      <ScrollProgressBar />\n\n      {/* Page wrapper */}\n      <div className=\"max-w-3xl mx-auto px-5 sm:px-8 xl:px-0 pb-20\">\n        {/* Header */}\n        <header className=\"pt-10 pb-8 border-b border-gray-200 dark:border-neutral-700 space-y-5\">\n          {/* Language badge */}\n          <motion.div\n            initial={{ opacity: 0, y: 8 }}\n            animate={{ opacity: 1, y: 0 }}\n            className=\"flex items-center gap-2\"\n          >\n            <div className=\"flex items-center gap-2 border border-gray-200 dark:border-neutral-700 px-2.5 py-1.5\">\n              <div className=\"relative w-4 h-4 flex-shrink-0\">\n                <Image\n                  fill\n                  alt={snippet.language.name}\n                  src={snippet.language.image.asset.url}\n                  className=\"object-contain\"\n                />\n              </div>\n              <span className=\"font-mono text-[10px] tracking-[0.35em] uppercase text-gray-500 dark:text-gray-400\">\n                {snippet.language.name}\n              </span>\n            </div>\n          </motion.div>\n\n          {/* Title */}\n          <motion.h1\n            initial={{ opacity: 0, y: 16 }}\n            animate={{ opacity: 1, y: 0 }}\n            transition={{ duration: 0.45, delay: 0.04 }}\n            className=\"text-4xl sm:text-5xl font-bold tracking-tight text-gray-900 dark:text-white leading-tight\"\n          >\n            {snippet.title}\n          </motion.h1>\n\n          {/* Excerpt */}\n          <motion.p\n            initial={{ opacity: 0, y: 10 }}\n            animate={{ opacity: 1, y: 0 }}\n            transition={{ duration: 0.45, delay: 0.1 }}\n            className=\"text-lg text-gray-600 dark:text-gray-400 border-l-2 border-gray-300 dark:border-gray-700 pl-4\"\n          >\n            {snippet.excerpt}\n          </motion.p>\n\n          {/* Meta row */}\n          <motion.div\n            initial={{ opacity: 0 }}\n            animate={{ opacity: 1 }}\n            transition={{ delay: 0.16 }}\n            className=\"flex flex-wrap items-center gap-x-4 gap-y-1 pt-1\"\n          >\n            <span className=\"font-mono text-[10px] tracking-[0.3em] uppercase text-gray-500 dark:text-gray-500\">\n              {getFormattedDate(new Date(snippet.publishedAt))}\n            </span>\n            <span className=\"text-gray-300 dark:text-gray-700\">·</span>\n            <span className=\"font-mono text-[10px] tracking-[0.3em] uppercase text-gray-500 dark:text-gray-500\">\n              {snippet.readingTime.text}\n            </span>\n          </motion.div>\n        </header>\n\n        {/* MDX Content */}\n        <div className=\"mt-10 max-w-full font-barlow prose-typography\">\n          {children}\n        </div>\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "lib/MDXContent.ts",
    "content": "import { FrontMatter } from \"./types\";\nimport matter from \"gray-matter\";\nimport path from \"path\";\nimport { readFileSync } from \"fs\";\nimport readTime from \"reading-time\";\nimport rehypeAutolinkHeadings from \"rehype-autolink-headings\";\nimport rehypePrettyCode from \"rehype-pretty-code\";\nimport rehypeSlug from \"rehype-slug\";\nimport { serialize } from \"next-mdx-remote/serialize\";\nimport { sync } from \"glob\";\n\nconst rehypePlugins = [\n  rehypeSlug,\n  [rehypeAutolinkHeadings, { behaviour: \"wrap\" }],\n  [\n    rehypePrettyCode,\n    {\n      theme: {\n        dark: \"andromeeda\",\n        light: \"catppuccin-latte\",\n      },\n      onVisitLine(node: any) {\n        if (node.children.length === 0) {\n          node.children = [{ type: \"text\", value: \" \" }];\n        }\n      },\n      onVisitHighlightedLine(node: any) {\n        node.properties.className.push(\"highlighted\");\n      },\n      onVisitHighlightedWord(node: any) {\n        node.properties.className = [\"word\"];\n      },\n    },\n  ],\n] as any;\n\nexport default class MDXContent {\n  private POST_PATH: string;\n  constructor(folderName: string) {\n    this.POST_PATH = path.join(process.cwd(), folderName);\n  }\n\n  getSlugs() {\n    const paths = sync(`${this.POST_PATH}/*.mdx`);\n    return paths.map((path) => {\n      const parts = path.split(\"/\");\n      const fileName = parts[parts.length - 1];\n      const [slug, _ext] = fileName.split(\".\");\n      return slug;\n    });\n  }\n\n  getFrontMatter(slug: string): FrontMatter | null {\n    const postPath = path.join(this.POST_PATH, `${slug}.mdx`);\n    const source = readFileSync(postPath);\n    const { content, data } = matter(source);\n    const readingTime = readTime(content);\n\n    if (!data.published) return null;\n\n    return {\n      slug,\n      readingTime,\n      excerpt: data.excerpt ?? \"\",\n      title: data.title ?? slug,\n      date: (data.date ?? new Date()).toString(),\n      keywords: data.keywords ?? \"\",\n      image: data.image ?? \"https://imgur.com/aNqa9cE.png\",\n      org: data.org ?? null,\n    };\n  }\n\n  async getPostFromSlug(slug: string, force: boolean = false) {\n    const postPath = path.join(this.POST_PATH, `${slug}.mdx`);\n    const source = readFileSync(postPath);\n    const { content, data } = matter(source);\n    if (!data.published && !force) return { post: null };\n\n    const frontMatter = this.getFrontMatter(slug);\n\n    const mdxSource = await serialize(content, {\n      mdxOptions: {\n        rehypePlugins,\n      },\n    });\n    return {\n      post: {\n        source: mdxSource,\n        tableOfContents: this.getTableOfContents(content),\n        meta: frontMatter,\n      },\n    };\n  }\n\n  getAllPosts(length?: number | undefined) {\n    const allPosts = this.getSlugs()\n      .map((slug) => {\n        return this.getFrontMatter(slug);\n      })\n      .filter((post) => post !== null) // Filter post if it is not published\n      .sort((a, b) => {\n        if (new Date(a!.date) > new Date(b!.date)) return -1;\n        if (new Date(a!.date) < new Date(b!.date)) return 1;\n        return 0;\n      });\n\n    return length === undefined ? allPosts : allPosts.slice(0, length);\n  }\n\n  getTableOfContents(markdown: string) {\n    const regXHeader = /#{2,6}.+/g;\n    const headingArray = markdown.match(regXHeader)\n      ? markdown.match(regXHeader)\n      : [];\n    return headingArray?.map((heading) => {\n      return {\n        level: heading.split(\"#\").length - 1 - 2, // we starts from the 2nd heading that's why we subtract 2 and 1 is extra heading text\n        heading: heading.replace(/#{2,6}/, \"\").trim(),\n      };\n    });\n  }\n}\n"
  },
  {
    "path": "lib/devto.ts",
    "content": "const PER_PAGE: number = 1000;\nconst DEV_API = process.env.NEXT_PUBLIC_BLOGS_API;\n\n/**\n * Makes a request to the DEV API to retrieve a specific page of followers for the user.\n */\nconst getPageOfFollowers = async (page: number) => {\n  // Make a request to the DEV API to retrieve a specific page of followers\n  const perPageFollowers = await fetch(\n    `https://dev.to/api/followers/users?per_page=${PER_PAGE}&page=${page}`,\n    {\n      headers: {\n        api_key: DEV_API!,\n      },\n    },\n  )\n    .then((response) => response.json())\n    .catch((err) => console.error(err));\n\n  return perPageFollowers.length;\n};\n\n/**\n * Makes multiple requests to the DEV API to retrieve all of the user's followers.\n */\nexport const allFollowers = async () => {\n  let numReturned = PER_PAGE;\n  let page = 1;\n  var totalFollowers = 0;\n\n  // Continue making requests to the DEV API until all followers have been retrieved\n  while (numReturned === PER_PAGE) {\n    const followers = await getPageOfFollowers(page);\n    totalFollowers += followers;\n    numReturned = followers;\n    page++;\n  }\n  return totalFollowers;\n};\n\n/**\n * Makes a request to the DEV API to retrieve a specific page of posts for the user.\n */\nconst getPageOfPosts = async (page: number) => {\n  // Make a request to the DEV API to retrieve a specific page of posts\n  const perPagePosts = await fetch(\n    `https://dev.to/api/articles/me?per_page=${PER_PAGE}&page=${page}`,\n    {\n      headers: {\n        api_key: DEV_API!,\n      },\n    },\n  )\n    .then((response) => response.json())\n    .catch((err) => console.error(err));\n  return perPagePosts;\n};\n\n/**\n * Makes multiple requests to the DEV API to retrieve all of the user's posts.\n */\nexport const allPosts = async () => {\n  let numReturned = PER_PAGE;\n  let page = 1;\n  var totalPosts = [];\n\n  // Continue making requests to the DEV API until all posts have been retrieved\n  while (numReturned === PER_PAGE) {\n    const posts = await getPageOfPosts(page);\n    totalPosts.push(...posts);\n    numReturned = posts.length;\n    page++;\n  }\n  return totalPosts;\n};\n"
  },
  {
    "path": "lib/fetcher.ts",
    "content": "/**\n * Makes a request to the specified URL and returns the response as JSON.\n */\nexport default async function fetcher(url: string) {\n  return fetch(url).then((r) => r.json());\n}\n"
  },
  {
    "path": "lib/generateRSS.ts",
    "content": "import RSS from \"rss\";\nimport { getAllPostsMeta } from \"./sanityContent\";\nimport { writeFileSync } from \"fs\";\n\nexport default async function getRSS() {\n  const siteURL = \"https://j471n.in\";\n  const allBlogs = await getAllPostsMeta();\n\n  // Create a new RSS object\n  const feed = new RSS({\n    title: \"Jatin Sharma\",\n    description: `I've been writing online since 2021, mostly about web development\n            and tech careers. In total, I've written ${allBlogs.length} articles\n            till now.`,\n    site_url: siteURL,\n    feed_url: `${siteURL}/feed.xml`,\n    language: \"en\",\n    pubDate: new Date(),\n    copyright: `All rights reserved ${new Date().getFullYear()}, Jatin Sharma`,\n  });\n\n  // Add all blog posts to the RSS feed\n  allBlogs?.map((post) => {\n    feed.item({\n      title: post.title,\n      url: `${siteURL}/blogs/${post.slug.current}`,\n      date: post.publishedAt,\n      description: post!.excerpt,\n    });\n  });\n\n  // Write the RSS feed to a file\n  writeFileSync(\"./public/feed.xml\", feed.xml({ indent: true }));\n}\n"
  },
  {
    "path": "lib/github.ts",
    "content": "import {\n  IContributionCalendar,\n  IContributionCountByDay,\n  IContributionDay,\n  IGitHubProfileResponse,\n  IGitHubRepositoriesAPIResponse,\n  IUserContributionDetails,\n  IWeek,\n} from \"./interface\";\n\nimport { GithubRepo } from \"./types\";\nimport moment from \"moment\";\n\nconst headers = new Headers({\n  Authorization: `token ${process.env.GITHUB_TOKEN}`,\n});\n\n// its for /api/stats/github\nexport async function fetchGithub(): Promise<IGitHubProfileResponse> {\n  const requestOptions: RequestInit = {\n    method: \"GET\",\n    headers,\n  };\n\n  try {\n    const response = await fetch(\n      \"https://api.github.com/users/j471n\",\n      requestOptions\n    );\n    if (!response.ok) {\n      throw new Error(\"Error fetching GitHub data: \" + response.statusText);\n    }\n    const data = await response.json(); // Await the JSON promise\n    return data as IGitHubProfileResponse;\n  } catch (error) {\n    console.error(error);\n    throw error;\n  }\n}\n\n/* Retrieves the number of stars and forks for the user's repositories on GitHub. */\nexport async function getGithubStarsAndForks() {\n  try {\n    // Fetch user's repositories from the GitHub API\n    const res = await fetch(\n      \"https://api.github.com/users/j471n/repos?per_page=100\",\n      { headers }\n    );\n    const userRepos: IGitHubRepositoriesAPIResponse[] = await res.json();\n\n    // filter those repos that are not forked\n    const mineRepos: GithubRepo[] = userRepos.filter(\n      (repo: GithubRepo) => !repo.fork\n    );\n\n    // Calculate the total number of stars for the user's repositories\n    const githubStars = mineRepos.reduce(\n      (accumulator: number, repository: GithubRepo) => {\n        return accumulator + repository[\"stargazers_count\"];\n      },\n      0\n    );\n\n    // Calculate the total number of forks for the user's repositories\n    const forks = mineRepos.reduce(\n      (accumulator: number, repository: GithubRepo) => {\n        return accumulator + repository[\"forks_count\"];\n      },\n      0\n    );\n\n    return { githubStars, forks };\n  } catch (error) {\n    console.error(error);\n    throw error;\n  }\n}\n\nexport async function getGithubContribution() {\n  const now = moment();\n  const from = moment(now).subtract(30, \"days\").utc().toISOString();\n  // also include the next day in case our server is behind in time with respect to GitHub\n  const to = moment(now).add(1, \"days\").utc().toISOString();\n  const q = {\n    query: `\n              query userInfo($LOGIN: String!, $FROM: DateTime!, $TO: DateTime!) {\n                user(login: $LOGIN) {\n                  name\n                  contributionsCollection(from: $FROM, to: $TO) {\n                    contributionCalendar {\n                      weeks {\n                        contributionDays {\n                          contributionCount\n                          date\n                        }\n                      }\n                    }\n                  }\n                }\n              }\n            `,\n    variables: {\n      LOGIN: \"j471n\",\n      FROM: from,\n      TO: to,\n    },\n  };\n\n  const response = await fetch(\"https://api.github.com/graphql\", {\n    method: \"POST\",\n    body: JSON.stringify(q),\n    headers,\n  });\n  const apiResponse = await response.json();\n\n  const userData: IUserContributionDetails = {\n    contributions: [],\n    name: apiResponse.data.user.name,\n  };\n\n  const weeks =\n    apiResponse.data.user.contributionsCollection.contributionCalendar.weeks;\n  weeks.map((week: IWeek) =>\n    week.contributionDays.map((contributionDay: IContributionDay) => {\n      contributionDay.shortDate = moment(contributionDay.date, moment.ISO_8601)\n        .date()\n        .toString();\n      userData.contributions.push(contributionDay);\n    })\n  );\n\n  const contributionCountByDayOfWeek = calculateMostProductiveDayOfWeek(\n    apiResponse.data.user.contributionsCollection.contributionCalendar\n  );\n\n  return { ...userData, contributionCountByDayOfWeek };\n}\n\n// Function to calculate the productive data by days\nfunction calculateMostProductiveDayOfWeek(\n  contributionCalendar: IContributionCalendar\n): { day: string; count: number }[] {\n  const daysOfWeek = [\n    \"Sunday\",\n    \"Monday\",\n    \"Tuesday\",\n    \"Wednesday\",\n    \"Thursday\",\n    \"Friday\",\n    \"Saturday\",\n  ];\n  const contributionCountByDayOfWeek: IContributionCountByDay = {\n    Sunday: 0,\n    Monday: 0,\n    Tuesday: 0,\n    Wednesday: 0,\n    Thursday: 0,\n    Friday: 0,\n    Saturday: 0,\n  };\n\n  for (const week of contributionCalendar.weeks) {\n    for (const day of week.contributionDays) {\n      const date = new Date(day.date);\n      const dayOfWeek = daysOfWeek[date.getUTCDay()];\n      contributionCountByDayOfWeek[dayOfWeek] += day.contributionCount;\n    }\n  }\n\n  const sortedData = Object.entries(contributionCountByDayOfWeek)\n    .sort((a, b) => daysOfWeek.indexOf(a[0]) - daysOfWeek.indexOf(b[0]))\n    .map(([day, count]) => ({ day, count }));\n\n  const sunday = sortedData.shift();\n\n  if (sunday) {\n    sortedData.push(sunday);\n  }\n\n  return sortedData;\n}\n"
  },
  {
    "path": "lib/hardcover.ts",
    "content": "import { HardcoverBook, BookStatusId, HardcoverProfile } from \"./types\";\n\nconst HARDCOVER_GRAPHQL_URL = \"https://api.hardcover.app/v1/graphql\";\n\n/* ---------- internal GraphQL helper ---------- */\n\nasync function hardcoverQuery<T>(\n  query: string,\n  variables?: Record<string, unknown>,\n): Promise<T> {\n  const res = await fetch(HARDCOVER_GRAPHQL_URL, {\n    method: \"POST\",\n    headers: {\n      \"Content-Type\": \"application/json\",\n      authorization: `Bearer ${process.env.HARDCOVER_API_KEY}`,\n    },\n    body: JSON.stringify({ query, variables }),\n  });\n\n  if (!res.ok) {\n    throw new Error(`Hardcover API responded with status ${res.status}`);\n  }\n\n  const json = await res.json();\n\n  if (json.errors?.length) {\n    throw new Error(json.errors[0].message);\n  }\n\n  return json.data as T;\n}\n\n/* ---------- helpers ---------- */\n\nasync function getMyUserId(): Promise<number> {\n  const data = await hardcoverQuery<{\n    me: Array<{ id: number }> | { id: number };\n  }>(`query { me { id } }`);\n\n  const me = data.me;\n  return Array.isArray(me) ? me[0].id : (me as { id: number }).id;\n}\n\n/* ---------- raw response shapes ---------- */\n\ntype RawUserBook = {\n  status_id: number;\n  updated_at: string | null;\n  rating: number | null;\n  dates_read: Array<{ finished_at: string | null }>;\n  book: {\n    id: number;\n    title: string | null;\n    subtitle: string | null;\n    slug: string | null;\n    pages: number | null;\n    release_year: number | null;\n    rating: number | null;\n    image: { url: string } | null;\n    contributions: Array<{ author: { name: string } }>;\n  };\n};\n\n/* ---------- public API ---------- */\n\nexport async function getMyBooks(): Promise<HardcoverBook[]> {\n  const userId = await getMyUserId();\n\n  const data = await hardcoverQuery<{ user_books: RawUserBook[] }>(\n    `query GetMyBooks($userId: Int!) {\n      user_books(\n        where: {\n          user_id: { _eq: $userId }\n          status_id: { _in: [1, 2, 3] }\n        }\n        order_by: { updated_at: desc }\n      ) {\n        status_id\n        updated_at\n        rating\n        book {\n          id\n          title\n          subtitle\n          slug\n          pages\n          release_year\n          rating\n          image { url }\n          contributions {\n            author { name }\n          }\n        }\n      }\n    }`,\n    { userId },\n  );\n\n  return data.user_books.map((ub) => ({\n    statusId: ub.status_id as BookStatusId,\n    id: ub.book.id,\n    title: ub.book.title ?? \"\",\n    subtitle: ub.book.subtitle ?? null,\n    slug: ub.book.slug ?? \"\",\n    pages: ub.book.pages ?? null,\n    releaseYear: ub.book.release_year ?? null,\n    rating: ub.book.rating ?? null,\n    coverUrl: ub.book.image?.url ?? null,\n    authors: ub.book.contributions.map((c) => c.author.name),\n    userRating: ub.rating ?? null,\n    updatedAt: ub.updated_at ?? null,\n    finishedAt: ub.dates_read?.[0]?.finished_at ?? null,\n  }));\n}\n\n/* ---------- profile + stats ---------- */\n\ntype RawGoal = {\n  goal: number;\n  progress: number;\n  metric: string;\n  state: string;\n  start_date: string;\n  end_date: string;\n};\n\ntype RawProfile = {\n  me: Array<{\n    username: string;\n    name: string | null;\n    books_count: number;\n    goals: RawGoal[];\n  }>;\n};\n\nexport async function getMyProfile(): Promise<HardcoverProfile> {\n  const currentYear = new Date().getFullYear();\n\n  const data = await hardcoverQuery<RawProfile>(\n    `query GetMyProfile {\n      me {\n        username\n        name\n        books_count\n        goals(\n          where: {\n            metric: { _eq: \"books\" }\n            start_date: { _gte: \"${currentYear}-01-01\" }\n            end_date:   { _lte: \"${currentYear}-12-31\" }\n          }\n          order_by: { start_date: desc }\n          limit: 1\n        ) {\n          goal\n          progress\n          metric\n          state\n          start_date\n          end_date\n        }\n      }\n    }`,\n  );\n\n  const me = data.me[0];\n  const goal = me.goals[0] ?? null;\n\n  return {\n    username: me.username,\n    name: me.name ?? me.username,\n    booksCount: me.books_count,\n    currentYearGoal: goal\n      ? {\n          target: goal.goal,\n          progress: Math.round(goal.progress),\n          metric: goal.metric,\n          state: goal.state,\n          year: currentYear,\n        }\n      : null,\n  };\n}\n"
  },
  {
    "path": "lib/instaposts.ts",
    "content": "import { getUserDataValue, setUserDataValue } from \"./supabase\";\n\nimport { InstagramData } from \"./interface\";\nimport { generateUrl } from \"@utils/functions\";\n\nexport async function generateNewAccessTokenInstagram(\n  token: string\n): Promise<string | undefined> {\n  const requestURL = generateUrl(\n    \"https://graph.instagram.com/refresh_access_token\",\n    {\n      grant_type: \"ig_refresh_token\",\n      access_token: token,\n    }\n  );\n\n  try {\n    const response = await fetch(requestURL);\n\n    if (response.ok) {\n      const result = await response.json();\n      const newToken = result?.access_token;\n      if (newToken) {\n        await setUserDataValue(\"instagram_user_token\", newToken);\n        return newToken;\n      }\n    } else {\n      return token;\n    }\n  } catch (e) {\n    console.error(\"Error : \", e);\n    return token;\n  }\n}\n\nexport async function getInstagramPosts(\n  additionalParams = {}\n): Promise<InstagramData[]> {\n  const { data: token } = await getUserDataValue(\"instagram_user_token\");\n\n  let outputData: InstagramData[] = [];\n\n  let requestParameters: any = {\n    fields: \"id,permalink,caption,media_url,thumbnail_url,media_type,timestamp\",\n    limit: 9,\n    access_token: token,\n    ...additionalParams,\n  };\n\n  const requestURL = generateUrl(\n    \"https://graph.instagram.com/me/media\",\n    requestParameters\n  );\n\n  try {\n    const response = await fetch(requestURL);\n\n    if (response.ok) {\n      const result = (await response.json()) as InstagramData[];\n      outputData = result;\n    }\n  } catch (e) {\n    console.error(\"error\", e);\n\n    const newToken = await generateNewAccessTokenInstagram(token);\n    requestParameters.access_token = newToken;\n    const requestURL = generateUrl(\n      \"https://graph.instagram.com/me/media\",\n      requestParameters\n    );\n    const response = await fetch(requestURL);\n\n    if (response.ok) {\n      const result = (await response.json()) as InstagramData[];\n      outputData = result;\n    }\n  }\n  return outputData;\n}\n"
  },
  {
    "path": "lib/interface/sanity.ts",
    "content": "import { ReadTimeResults } from \"reading-time\";\nimport { SanityDocument } from \"@sanity/types\";\nimport { TableOfContents as TableOfContentsType } from \"@lib/types\";\n\nexport interface ISanityImage {\n  asset: {\n    _ref: string;\n    _type: \"reference\";\n    url: \"string\";\n  };\n}\nexport interface BlogPost extends SanityDocument {\n  _id: string;\n  title: string;\n  slug: {\n    current: string;\n  };\n  keywords: string;\n  excerpt: string;\n  image_url: string;\n  mainImage: ISanityImage;\n  publishedAt: string;\n  author: {\n    name: string;\n    image: ISanityImage;\n  };\n  organization: {\n    name: string;\n    image: ISanityImage;\n    website: string;\n  };\n  content?: any;\n  readingTime: ReadTimeResults;\n  tableOfContents: TableOfContentsType[];\n}\n\nexport interface ISnippet extends SanityDocument {\n  _id: string;\n  title: string;\n  slug: {\n    current: string;\n  };\n  publishedAt: string;\n  excerpt: string;\n  language: {\n    name: string;\n    image: ISanityImage;\n  };\n  content?: any;\n  readingTime: ReadTimeResults;\n  tableOfContents: {\n    level: number;\n    heading: string;\n  }[];\n}\n\nexport interface IStaticPage extends SanityDocument {\n  _id: string;\n  title: string;\n  slug: {\n    current: string;\n  };\n  keywords: string;\n  excerpt: string;\n  mainImage: ISanityImage;\n  publishedAt: string;\n  content?: any;\n}\n\nexport type EpigraphSourceType =\n  | \"book\"\n  | \"movie\"\n  | \"tvShow\"\n  | \"person\"\n  | \"song\"\n  | \"podcast\"\n  | \"other\";\n\nexport interface IEpigraph extends SanityDocument {\n  _id: string;\n  quote: string;\n  sourceType: EpigraphSourceType;\n  sourceTitle: string;\n  sourceMeta?: string;\n  speaker?: string;\n  year?: number;\n  tags?: string[];\n  addedAt: string;\n}\n\n/* \n{\n    publishedAt: '2023-07-04T21:15:26.932Z',\n    _id: 'eb0cb39f-1d0a-4285-a3af-7357968bb1d6',\n    title: 'DarkMode Context API',\n    slug: { current: 'darkmode-context-api', _type: 'slug' },\n    excerpt: 'It enables to add dark mode switch.',\n    language: { name: 'React', image: [Object] }\n  }\n\n\n*/\n"
  },
  {
    "path": "lib/interface.ts",
    "content": "export interface IExternalUrls {\n  spotify: string;\n}\nexport interface IFollowers {\n  href?: null;\n  total: number;\n}\nexport interface IImagesEntity {\n  height: number;\n  url: string;\n  width: number;\n}\n\nexport interface IArtistsAPIResponse {\n  external_urls: IExternalUrls;\n  followers: IFollowers;\n  genres?: string[] | null;\n  href: string;\n  id: string;\n  images?: IImagesEntity[] | null;\n  name: string;\n  popularity: number;\n  type: string;\n  uri: string;\n}\n\nexport interface ISpotifyArtist {\n  external_urls: IExternalUrls;\n  href: string;\n  id: string;\n  name: string;\n  type: string;\n  uri: string;\n}\n\nexport interface ISpotifyAlbum {\n  album_type: string;\n  artists: ISpotifyAlbum[];\n  available_markets: string[];\n  external_urls: IExternalUrls;\n  href: string;\n  id: string;\n  images: IImagesEntity[];\n  name: string;\n  release_date: string;\n  release_date_precision: string;\n  total_tracks: number;\n  type: string;\n  uri: string;\n}\n\nexport interface ITracksAPIResponse {\n  album: ISpotifyAlbum;\n  artists: ISpotifyAlbum[];\n  available_markets: string[];\n  disc_number: number;\n  duration_ms: number;\n  explicit: boolean;\n  external_urls: IExternalUrls;\n  href: string;\n  id: string;\n  is_local: boolean;\n  name: string;\n  popularity: number;\n  preview_url?: string;\n  track_number: number;\n  type: string;\n  uri: string;\n}\n\nexport interface IGitHubProfileResponse {\n  login: string;\n  id: number;\n  node_id: string;\n  avatar_url: string;\n  gravatar_id: string;\n  url: string;\n  html_url: string;\n  followers_url: string;\n  following_url: string;\n  gists_url: string;\n  starred_url: string;\n  subscriptions_url: string;\n  organizations_url: string;\n  repos_url: string;\n  events_url: string;\n  received_events_url: string;\n  type: string;\n  site_admin: boolean;\n  name: string;\n  company: any;\n  blog: string;\n  location: string;\n  email: any;\n  hireable: boolean;\n  bio: string;\n  twitter_username: string;\n  public_repos: number;\n  public_gists: number;\n  followers: number;\n  following: number;\n  created_at: string;\n  updated_at: string;\n}\n\nexport interface IGitHubOwner {\n  login: string;\n  id: number;\n  node_id: string;\n  avatar_url: string;\n  gravatar_id: string;\n  url: string;\n  html_url: string;\n  followers_url: string;\n  following_url: string;\n  gists_url: string;\n  starred_url: string;\n  subscriptions_url: string;\n  organizations_url: string;\n  repos_url: string;\n  events_url: string;\n  received_events_url: string;\n  type: string;\n  site_admin: boolean;\n}\n\nexport interface IGitHubLicense {\n  key: string;\n  name: string;\n  spdx_id: string;\n  url: string;\n  node_id: string;\n}\n\nexport interface IGitHubRepositoriesAPIResponse {\n  id: number;\n  node_id: string;\n  name: string;\n  full_name: string;\n  private: boolean;\n  owner: IGitHubOwner;\n  html_url: string;\n  description?: string;\n  fork: boolean;\n  url: string;\n  forks_url: string;\n  keys_url: string;\n  collaborators_url: string;\n  teams_url: string;\n  hooks_url: string;\n  issue_events_url: string;\n  events_url: string;\n  assignees_url: string;\n  branches_url: string;\n  tags_url: string;\n  blobs_url: string;\n  git_tags_url: string;\n  git_refs_url: string;\n  trees_url: string;\n  statuses_url: string;\n  languages_url: string;\n  stargazers_url: string;\n  contributors_url: string;\n  subscribers_url: string;\n  subscription_url: string;\n  commits_url: string;\n  git_commits_url: string;\n  comments_url: string;\n  issue_comment_url: string;\n  contents_url: string;\n  compare_url: string;\n  merges_url: string;\n  archive_url: string;\n  downloads_url: string;\n  issues_url: string;\n  pulls_url: string;\n  milestones_url: string;\n  notifications_url: string;\n  labels_url: string;\n  releases_url: string;\n  deployments_url: string;\n  created_at: string;\n  updated_at: string;\n  pushed_at: string;\n  git_url: string;\n  ssh_url: string;\n  clone_url: string;\n  svn_url: string;\n  homepage?: string;\n  size: number;\n  stargazers_count: number;\n  watchers_count: number;\n  language?: string;\n  has_issues: boolean;\n  has_projects: boolean;\n  has_downloads: boolean;\n  has_wiki: boolean;\n  has_pages: boolean;\n  has_discussions: boolean;\n  forks_count: number;\n  mirror_url: any;\n  archived: boolean;\n  disabled: boolean;\n  open_issues_count: number;\n  license?: IGitHubLicense;\n  allow_forking: boolean;\n  is_template: boolean;\n  web_commit_signoff_required: boolean;\n  topics: string[];\n  visibility: string;\n  forks: number;\n  open_issues: number;\n  watchers: number;\n  default_branch: string;\n}\n\nexport interface IContributionDay {\n  contributionCount: number;\n  date: string;\n  shortDate: string;\n}\n\nexport interface IWeek {\n  contributionDays: IContributionDay[];\n}\n\nexport interface IUserContributionDetails {\n  contributions: IContributionDay[];\n  name: string;\n}\n\nexport interface IContributionCalendar {\n  weeks: IWeek[];\n}\n\nexport interface IContributionCountByDay {\n  [day: string]: number;\n}\n\nexport interface IEmailValidation {\n  valid: boolean;\n  block: boolean;\n  disposable: boolean;\n  domain: string;\n  text: string;\n  reason: string;\n  risk: number;\n  mx_host: string;\n  possible_typo: any[];\n  mx_ip: string;\n  mx_info: string;\n  last_changed_at: string;\n}\n\n/* Linkedin Interface */\nexport interface ILinkedinResponse {\n  public_identifier: string;\n  profile_pic_url: string;\n  background_cover_image_url: string;\n  first_name: string;\n  last_name: string;\n  full_name: string;\n  follower_count: number;\n  occupation: string;\n  headline: string;\n  summary: string;\n  country: string;\n  country_full_name: string;\n  city: string;\n  state: string;\n  experiences: ILinkedInExperience[];\n  education: ILinkedInEducation[];\n  languages: string[];\n  accomplishment_organisations: any[];\n  accomplishment_publications: any[];\n  accomplishment_honors_awards: any[];\n  accomplishment_patents: any[];\n  accomplishment_courses: any[];\n  accomplishment_projects: any[];\n  accomplishment_test_scores: any[];\n  volunteer_work: ILinkedInVolunteerWork[];\n  certifications: ILinkedInCertification[];\n  connections: any;\n  recommendations: any[];\n  activities: any[];\n  articles: any[];\n  groups: any[];\n  phone_numbers: any[];\n  social_networking_services: any[];\n  skills: string[];\n  gender: any;\n  birth_date: any;\n  industry: any;\n  interests: any[];\n  personal_emails: any[];\n  personal_numbers: any[];\n}\n\nexport interface ILinkedInExperience {\n  company: string;\n  company_linkedin_profile_url: string;\n  logo_url: string;\n  job_titles: {\n    starts_at: ILinkedInStartsAt;\n    ends_at?: ILinkedInEndsAt;\n    title: string;\n    description?: string;\n    location?: string;\n  }[];\n}\n\nexport interface ILinkedInStartsAt {\n  day: number;\n  month: number;\n  year: number;\n}\n\nexport interface ILinkedInEndsAt {\n  day: number;\n  month: number;\n  year: number;\n}\n\nexport interface ILinkedInEducation {\n  starts_at: ILinkedInStartsAt;\n  ends_at: ILinkedInEndsAt;\n  field_of_study: string;\n  degree_name: string;\n  school: string;\n  school_linkedin_profile_url: any;\n  description: any;\n  logo_url: string;\n  grade: any;\n  activities_and_societies: any;\n}\n\nexport interface ILinkedInVolunteerWork {\n  starts_at: ILinkedInStartsAt;\n  ends_at: ILinkedInEndsAt;\n  title: string;\n  cause: string;\n  company: string;\n  company_linkedin_profile_url: string;\n  description?: string;\n  logo_url: string;\n}\n\nexport interface ILinkedInCertification {\n  starts_at: ILinkedInStartsAt;\n  ends_at: ILinkedInEndsAt;\n  name: string;\n  license_number?: string;\n  display_source: string;\n  authority: string;\n  url: string;\n}\n\nexport interface ITMDBData {\n  adult: boolean;\n  backdrop_path: string;\n  genre_ids: number[];\n  id: number;\n  original_language: string;\n  original_title?: string;\n  original_name?: string;\n  overview: string;\n  popularity: number;\n  poster_path: string;\n  release_date?: string;\n  first_air_date?: string;\n  title: string;\n  name?: string;\n  video: boolean;\n  vote_average: number;\n  vote_count: number;\n  rating?: number;\n}\n\n/* ----------------------------------INSTAGRAM------------------------------ */\n\nexport enum MediaType {\n  CarouselAlbum = \"CAROUSEL_ALBUM\",\n  Image = \"IMAGE\",\n  Video = \"VIDEO\",\n}\nexport interface DetailedInstagramPost {\n  id: string;\n  permalink: string;\n  caption: string;\n  media_url: string;\n  media_type: MediaType;\n  timestamp: string;\n  thumbnail_url?: string;\n}\n\nexport interface Cursors {\n  before: string;\n  after: string;\n}\nexport interface Paging {\n  cursors: Cursors;\n  next: string;\n  previous: string;\n}\n\nexport interface InstagramData {\n  data: DetailedInstagramPost[];\n  paging: Paging;\n}\n"
  },
  {
    "path": "lib/sanityClient.ts",
    "content": "import { createClient } from \"@sanity/client\";\n\nconst client = createClient({\n  projectId: process.env.SANITY_PROJECT_ID, // you can find this in sanity.json\n  dataset: \"production\", // or the name you chose in step 1\n  useCdn: true, // `false` if you want to ensure fresh data\n  apiVersion: \"2021-08-31\",\n});\n\nexport default client;\n"
  },
  {
    "path": "lib/sanityContent.ts",
    "content": "import { BlogPost, IEpigraph, ISnippet } from \"./interface/sanity\";\n\nimport groq from \"groq\";\nimport matter from \"gray-matter\";\nimport readTime from \"reading-time\";\nimport rehypeAutolinkHeadings from \"rehype-autolink-headings\";\nimport rehypePrettyCode from \"rehype-pretty-code\";\nimport rehypeSlug from \"rehype-slug\";\nimport sanityClient from \"@lib/sanityClient\";\nimport { serialize } from \"next-mdx-remote/serialize\";\n\nconst rehypePlugins = [\n  rehypeSlug,\n  [rehypeAutolinkHeadings, { behaviour: \"wrap\" }],\n  [\n    rehypePrettyCode,\n    {\n      theme: {\n        dark: \"github-dark\",\n        light: \"github-light\",\n      },\n      onVisitLine(node: any) {\n        if (node.children.length === 0) {\n          node.children = [{ type: \"text\", value: \" \" }];\n        }\n      },\n      onVisitHighlightedLine(node: any) {\n        node.properties.className.push(\"highlighted\");\n      },\n      onVisitHighlightedWord(node: any) {\n        node.properties.className = [\"word\"];\n      },\n    },\n  ],\n] as any;\n\nexport async function getPostCount(): Promise<number> {\n  const query = groq`count(*[_type == \"post\"])`;\n  return sanityClient.fetch(query);\n}\n\nexport async function getAllPostsMeta(limit?: number): Promise<BlogPost[]> {\n  const query = groq`*[_type == \"post\"] | order(publishedAt desc)${\n    limit ? `[0..${limit - 1}]` : \"\"\n  } {\n    _id,\n    title,\n    slug,\n    keywords,\n    excerpt,\n    mainImage {\n      asset->{\n        _id,\n        url\n      }\n    },\n    publishedAt,\n    author->{name, image {asset -> {_id, url}}},\n    organization->{name, image {asset -> {_id, url}}, website},\n  }`;\n\n  const res = await sanityClient.fetch(query);\n  return res;\n}\n\nexport async function getAllSnippetsMeta(limit?: number): Promise<ISnippet[]> {\n  const query = groq`*[_type == \"snippet\"] | order(publishedAt desc)${\n    limit ? `[0..${limit - 1}]` : \"\"\n  } {\n    _id,\n    title,\n    slug,\n    excerpt,\n    language->{name, image {asset -> {_id, url}}},\n    publishedAt,\n  }`;\n\n  const res = await sanityClient.fetch(query);\n  return res;\n}\n\nexport async function getAllSlugs({\n  type,\n}: {\n  type: \"post\" | \"snippet\";\n}): Promise<string[]> {\n  const query = groq`*[_type == \"${type}\"] | order(publishedAt desc) {\n    slug {\n      current\n    }\n  }`;\n\n  const res_slugs = await sanityClient.fetch(query);\n  const slugs = res_slugs.map((item: any) => {\n    return item.slug.current;\n  });\n  return slugs;\n}\n\nexport async function getPostFromSlug(slug: string) {\n  const query = groq`*[_type == \"post\" && slug.current == \"${slug}\"][0] {\n    _id,\n    title,\n    slug,\n    keywords,\n    excerpt,\n    image_url,\n    mainImage {\n      asset->{\n        _id,\n        url\n      }\n    },\n    _createdAt,\n    publishedAt,\n    author->{name, image {asset -> {_id, url}}},\n    organization->{name, image {asset -> {_id, url}}, website},\n    content\n  }`;\n\n  const post = await sanityClient.fetch(query);\n\n  const source = post.content;\n  const { content } = matter(source);\n  const readingTime = readTime(content);\n\n  const tableOfContents = getTableOfContents(content);\n  const mdxSource = await getMarkdownSource(content);\n\n  post[\"content\"] = mdxSource;\n  post[\"tableOfContents\"] = tableOfContents;\n  post[\"readingTime\"] = readingTime;\n\n  return post;\n}\nexport async function getSnippetFromSlug(slug: string) {\n  const query = groq`*[_type == \"snippet\" && slug.current == \"${slug}\"][0] {\n    _id,\n    title,\n    slug,\n    excerpt,\n    publishedAt,\n    language->{name, image {asset -> {_id, url}}},\n    content\n  }`;\n\n  const post = await sanityClient.fetch(query);\n\n  const source = post.content;\n  const { content } = matter(source);\n  const readingTime = readTime(content);\n\n  const tableOfContents = getTableOfContents(content);\n  const mdxSource = await getMarkdownSource(content);\n\n  post[\"content\"] = mdxSource;\n  post[\"tableOfContents\"] = tableOfContents;\n  post[\"readingTime\"] = readingTime;\n\n  return post;\n}\n\nexport async function getStaticPageFromSlug(slug: string) {\n  const query = groq`*[_type == \"static_page\" && slug.current == \"${slug}\"][0] {\n    _id,\n    title,\n    slug,\n    keywords,\n    excerpt,\n    mainImage {\n      asset->{\n        _id,\n        url\n      }\n    },\n    publishedAt,\n    content\n  }`;\n\n  const post = await sanityClient.fetch(query);\n\n  const source = post.content;\n  const { content } = matter(source);\n\n  const mdxSource = await getMarkdownSource(content);\n\n  post[\"content\"] = mdxSource;\n  return post;\n}\n\nexport function getTableOfContents(markdown: string) {\n  const regXHeader = /#{2,6}.+/g;\n  const headingArray = markdown.match(regXHeader)\n    ? markdown.match(regXHeader)\n    : [];\n\n  const headingCounts = new Map<string, number>();\n\n  return headingArray?.map((heading) => {\n    const cleanHeading = heading.replace(/#{2,6}/, \"\").trim();\n    let suffix = \"\";\n\n    if (headingCounts.has(cleanHeading)) {\n      const count = headingCounts.get(cleanHeading)! + 1;\n      headingCounts.set(cleanHeading, count);\n      suffix = `-${count}`;\n    } else {\n      headingCounts.set(cleanHeading, 0);\n    }\n\n    return {\n      level: heading.split(\"#\").length - 1 - 2,\n      id: cleanHeading + suffix,\n      heading: heading.replace(/#{2,6}/, \"\").trim(),\n    };\n  });\n}\n\nexport async function getMarkdownSource(content: string) {\n  const source = await serialize(content, {\n    mdxOptions: {\n      rehypePlugins,\n    },\n  });\n  return source;\n}\n\nexport async function getEpigraphCount(): Promise<number> {\n  const query = groq`count(*[_type == \"epigraph\"])`;\n  return sanityClient.fetch(query);\n}\n\nexport async function getAllEpigraphs(limit?: number): Promise<IEpigraph[]> {\n  const query = groq`*[_type == \"epigraph\"] | order(addedAt desc)${\n    limit ? `[0..${limit - 1}]` : \"\"\n  } {\n    _id,\n    quote,\n    sourceType,\n    sourceTitle,\n    sourceMeta,\n    speaker,\n    year,\n    tags,\n    addedAt,\n  }`;\n  return sanityClient.fetch(query);\n}\n"
  },
  {
    "path": "lib/sitemap.ts",
    "content": "import { getAllSlugs } from \"./sanityContent\";\nimport { globby } from \"globby\";\nimport { writeFileSync } from \"fs\";\n\nexport default async function generate() {\n  const pages = await globby([\n    \"pages/*.tsx\",\n    \"!pages/_*.tsx\",\n    \"!pages/api\",\n    \"!pages/404.tsx\",\n  ]);\n\n  const postsSlugs = (await getAllSlugs({ type: \"post\" })).map(\n    (item) => `/blogs/${item}`\n  );\n  const snippetsSlugs = (await getAllSlugs({ type: \"snippet\" })).map(\n    (item) => `/snippets/${item}`\n  );\n\n  const pagesRoute = pages.map((page) => {\n    const path = page.replace(\"pages\", \"\").replace(\".tsx\", \"\");\n    const route = path === \"/index\" ? \"\" : path;\n    return route;\n  });\n\n  const sitemap = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n    <urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n        ${[...pagesRoute, ...postsSlugs, ...snippetsSlugs]\n          .map((route) => {\n            return `\n              <url>\n                  <loc>${`https://j471n.in${route}`}</loc>\n              </url>\n            `;\n          })\n          .join(\"\")}\n    </urlset>\n    `;\n\n  // eslint-disable-next-line no-sync\n  writeFileSync(\"public/sitemap.xml\", sitemap);\n}\n"
  },
  {
    "path": "lib/spotify.ts",
    "content": "import { IArtistsAPIResponse, ITracksAPIResponse } from \"./interface\";\nimport { SpotifyAccessToken } from \"./types\";\n\nconst client_id = process.env.SPOTIFY_CLIENT_ID;\nconst client_secret = process.env.SPOTIFY_CLIENT_SECRET;\nconst refresh_token = process.env.SPOTIFY_REFRESH_TOKEN;\n\n/**\n * Makes a request to the Spotify API to obtain a new access token using a refresh token.\n */\nconst getAccessToken = async (): Promise<SpotifyAccessToken> => {\n  // Make a POST request to the Spotify API to request a new access token\n  const response = await fetch(\"https://accounts.spotify.com/api/token\", {\n    method: \"POST\",\n    headers: {\n      // Set the Authorization header with the client ID and client secret encoded in base64\n      Authorization: `Basic ${Buffer.from(\n        `${client_id}:${client_secret}`\n      ).toString(\"base64\")}`,\n      \"Content-Type\": \"application/x-www-form-urlencoded\",\n    },\n    // Set the body of the request to include the refresh token and grant type\n    body: new URLSearchParams({\n      grant_type: \"refresh_token\",\n      refresh_token: refresh_token!,\n    }),\n  });\n\n  // Return the JSON response from the API\n  return response.json();\n};\n\n/**\n * Makes a request to the Spotify API to retrieve the user's top tracks.\n */\nexport const topTracks = async (): Promise<ITracksAPIResponse[]> => {\n  // Obtain an access token\n  const { access_token }: { access_token: string } = await getAccessToken();\n\n  // Make a request to the Spotify API to retrieve the user's top tracks in last 4 weeks\n\n  const response = await fetch(\n    \"https://api.spotify.com/v1/me/top/tracks?limit=10&time_range=short_term\",\n    {\n      headers: {\n        // Set the Authorization header with the access token\n        Authorization: `Bearer ${access_token}`,\n      },\n    }\n  );\n\n  // Handle the response and convert it to the expected type\n  if (!response.ok) {\n    throw new Error(\"Failed to fetch top artists.\");\n  }\n  const data = await response.json();\n  return data.items as ITracksAPIResponse[];\n};\n\n/**\n * Makes a request to the Spotify API to retrieve the user's top artists.\n */\nexport const topArtists = async (): Promise<IArtistsAPIResponse[]> => {\n  // Obtain an access token\n  const { access_token } = await getAccessToken();\n\n  // Make a request to the Spotify API to retrieve the user's top artists in last 4 weeks\n  const response = await fetch(\n    \"https://api.spotify.com/v1/me/top/artists?limit=5&time_range=short_term\",\n    {\n      headers: {\n        // Set the Authorization header with the access token\n        Authorization: `Bearer ${access_token}`,\n      },\n    }\n  );\n\n  // Handle the response and convert it to the expected type\n  if (!response.ok) {\n    throw new Error(\"Failed to fetch top artists.\");\n  }\n\n  const data = await response.json();\n  return data.items as IArtistsAPIResponse[];\n};\n\n/**\n * Makes a request to the Spotify API to retrieve the currently playing song for the user.\n */\nexport const currentlyPlayingSong = async () => {\n  // Obtain an access token\n  const { access_token } = await getAccessToken();\n\n  // Make a request to the Spotify API to retrieve the currently playing song for the user\n  return fetch(\"https://api.spotify.com/v1/me/player/currently-playing\", {\n    headers: {\n      // Set the Authorization header with the access token\n      Authorization: `Bearer ${access_token}`,\n    },\n  });\n};\n"
  },
  {
    "path": "lib/supabase.ts",
    "content": "import { createClient } from \"@supabase/supabase-js\";\n\n// A Supabase client object for making requests to a Supabase server.\nexport const supabase = createClient(\n  process.env.SUPABASE_URL!,\n  process.env.SUPABASE_KEY!,\n);\n\n/**\n * Asynchronously fetches all projects from the database where the 'pinned' column is set to true.\n * The results are sorted by the 'created_at' column in descending order.\n */\nexport async function getProjects() {\n  let { data: projects, error } = await supabase\n    .from(\"projects\")\n    .select(\"*\")\n    .eq(\"pinned\", \"true\")\n    .order(\"created_at\", { ascending: false });\n\n  return {\n    projects,\n    error: error !== null,\n  };\n}\n\n/**\n * Asynchronously fetches all certificates from the database where the 'pinned' column is set to true.\n * The results are sorted by the 'created_at' column in descending order.\n */\nexport async function getCertificates() {\n  let { data: certificates, error } = await supabase\n    .from(\"certificates\")\n    .select(\"*\")\n    .eq(\"pinned\", \"true\")\n    .order(\"created_at\", { ascending: false });\n\n  return {\n    certificates,\n    error: error !== null,\n  };\n}\n\n/**\n * This function is used to add a view to the specified blog post. It first retrieves the blog post from the database\n * by its slug value. If the post exists, it increments the view count by 1 and updates it in the database.\n * If the post does not exist, it creates a new record with the slug and views set to 1.\n */\nexport async function addView(slug: string) {\n  try {\n    const blogSlug = await getViewBySlug(slug);\n\n    if (blogSlug !== undefined) {\n      return await supabase\n        .from(\"views\")\n        .update({ views: blogSlug.views + 1 })\n        .eq(\"slug\", slug);\n    } else {\n      return await supabase.from(\"views\").insert({\n        slug: slug,\n        views: 1,\n      });\n    }\n  } catch (err) {\n    console.error(err);\n  }\n}\n\n/**\n * This function is used to retrieve the view count of a specified blog post by its slug value.\n * It queries the database and selects the \"views\" field for the record with a matching \"slug\" value.\n */\nexport async function getViewBySlug(slug: string) {\n  try {\n    const { data } = await supabase\n      .from(\"views\")\n      .select(\"views\")\n      .eq(\"slug\", slug);\n    return data![0];\n  } catch (error) {\n    console.error(error);\n  }\n}\n\n/**\n *\n * This function is used to retrieve all the views count and all the blog post in the database.\n * It first retrieves the total views count using a predefined function \"views_sum\" in supabase.\n * Then it retrieves all the records from \"views\" table.\n */\nexport async function getAllViews() {\n  try {\n    // views_sum is defined in supabase\n    const { data: totalViews } = await supabase.rpc(\"views_sum\");\n    const { data: posts } = await supabase.from(\"views\").select(\"*\");\n\n    return {\n      totalViews,\n      posts,\n    };\n  } catch (error) {\n    console.error(error);\n  }\n}\n/*\n * This function will retrieve the individual custom data from the supabase such as linkedin data\n */\nexport async function getUserDataValue(key: string) {\n  let { data, error } = await supabase\n    .from(\"user_data\")\n    .select(\"value\")\n    .eq(\"key\", key)\n    .limit(1)\n    .order(\"created_at\", { ascending: false });\n\n  if (data?.length === 0) {\n    return {\n      data: null,\n      error: null,\n    };\n  }\n  return {\n    data: data![0].value,\n    error: error !== null,\n  };\n}\n\nexport async function setUserDataValue(key: string, value1: any) {\n  const { data, error } = await supabase\n    .from(\"user_data\")\n    .update({ value: value1 })\n    .eq(\"key\", key)\n    .select();\n\n  if (data?.length === 0) {\n    return {\n      data: null,\n      error: null,\n    };\n  }\n  return {\n    data: data![0].value,\n    error: error !== null,\n  };\n}\n"
  },
  {
    "path": "lib/tmdb.ts",
    "content": "import { ITMDBData } from \"./interface\";\n\nconst options: RequestInit = {\n  method: \"GET\",\n  headers: {\n    accept: \"application/json\",\n    Authorization: `Bearer ${process.env.TMDB_ACCESS_TOKEN}`,\n  },\n};\n\nasync function fetchData(url: string): Promise<ITMDBData[]> {\n  try {\n    const response = await fetch(url, {\n      ...options,\n      signal: AbortSignal.timeout(5000), // 5 second timeout (fast-fail)\n    });\n    if (!response.ok) {\n      throw new Error(\"Error while fetching TMDB data: \" + response.statusText);\n    }\n    const data = await response.json();\n    return (data?.results ?? data?.items) as ITMDBData[];\n  } catch (error) {\n    console.error(error);\n    return [];\n  }\n}\n\n/**\n * Fetch TMDB data from multiple endpoints and combine the results.\n */\nexport async function fetchTMDBData(): Promise<ITMDBData[]> {\n  try {\n    const accountId = process.env.TMDB_ACCOUNT_ID;\n\n    // Call all three APIs concurrently using Promise.all\n    const [recentRatedMovies, recentRatedTvShows, watchingData] =\n      await Promise.all([\n        fetchData(\n          `https://api.themoviedb.org/3/account/${accountId}/rated/movies?sort_by=created_at.desc`,\n        ),\n        fetchData(\n          `https://api.themoviedb.org/3/account/${accountId}/rated/tv?sort_by=created_at.desc`,\n        ),\n        fetchData(`https://api.themoviedb.org/3/list/8261150`),\n      ]);\n\n    // Combine the results from the APIs\n    const combinedData = [\n      ...recentRatedMovies.slice(0, 3),\n      ...recentRatedTvShows.slice(0, 3),\n    ];\n\n    return [...watchingData.reverse(), ...combinedData];\n  } catch (error) {\n    console.error(error);\n    return [];\n  }\n}\n"
  },
  {
    "path": "lib/toc.ts",
    "content": "/**\n * Converts a string to a slug by lowercasing it, trimming leading and trailing whitespace,\n * replacing any non-word or non-space characters with an empty string, replacing all contiguous whitespace\n * with a single hyphen, and removing any hyphens from the beginning or end of the resulting string.\n */\nexport function stringToSlug(str: string) {\n  return str\n    .toLowerCase()\n    .trim()\n    .replace(/[^\\w\\s-]/g, \"\")\n    .replace(/[\\s]+/g, \"-\")\n    .replace(/^-+|-+$/g, \"\");\n}\n"
  },
  {
    "path": "lib/types.ts",
    "content": "import { IconType } from \"react-icons/lib\";\nimport { MDXRemoteSerializeResult } from \"next-mdx-remote\";\nimport React from \"react\";\nimport { ReadTimeResults } from \"reading-time\";\nimport { Variants } from \"framer-motion\";\n\n/* Custom Animated Components types */\nexport type AnimatedTAGProps = {\n  variants: Variants;\n  className?: string;\n  children: React.ReactNode;\n  infinity?: boolean;\n};\n\n/* Spotify Track  */\nexport type SpotifyTrack = {\n  id: number;\n  title: string;\n  url: string;\n  coverImage: {\n    url: string;\n  };\n  artist: string;\n};\n\n/* Spotify Artist  */\nexport type SpotifyArtist = {\n  id: number;\n  name: string;\n  url: string;\n  coverImage: {\n    url: string;\n  };\n  popularity: number;\n};\n\nexport type ProjectType = {\n  id: string;\n  name: string;\n  coverImage: string;\n  description: string;\n  githubURL: string;\n  previewURL?: string;\n  tools?: string[];\n  pinned?: boolean;\n};\n\nexport type SkillType = {\n  name: string;\n  Icon: IconType;\n  level?: number; // Proficiency level 1-100\n  category?: string; // Frontend, Backend, Database, etc.\n};\n\nexport type CertificateType = {\n  id: string;\n  title: string;\n  issuedDate: string;\n  orgName: string;\n  orgLogo: string;\n  url: string;\n  pinned: boolean;\n};\n\nexport type SocialPlatform = {\n  title: string;\n  Icon: IconType;\n  url: string;\n};\n\nexport type UtilityType = {\n  title: string;\n  data: {\n    name: string;\n    description: string;\n    Icon: IconType | JSX.Element;\n    link: string;\n  }[];\n};\n\nexport type Utilities = {\n  title: string;\n  description: string;\n  lastUpdate: string;\n  data: UtilityType[];\n};\n\nexport type FrontMatter = {\n  slug: string;\n  readingTime: ReadTimeResults;\n  excerpt: string;\n  title: string;\n  date: string;\n  keywords: string;\n  image: string;\n  org?: string | null;\n};\n\nexport type PostType = {\n  meta: FrontMatter;\n  source: MDXRemoteSerializeResult;\n  tableOfContents: TableOfContents[];\n};\n\nexport type TableOfContents = {\n  level: number;\n  id: string;\n  heading: string;\n};\n\nexport type SupportMe = {\n  name: string;\n  url: string;\n  Icon: IconType;\n};\n\nexport type Song = {\n  album: string;\n  artist: string;\n  albumImageUrl: string;\n  isPlaying: boolean;\n  songUrl: string;\n  title: string;\n};\n\nexport type FormInput = {\n  to_name: string;\n  first_name: string;\n  last_name: string;\n  email: string;\n  subject: string;\n  message: string;\n};\n\nexport type SpotifyAccessToken = {\n  access_token: string;\n};\n\nexport type GithubRepo = {\n  stargazers_count: number;\n  fork: boolean;\n  forks_count: number;\n};\n\nexport type PageData = {\n  title: string;\n  description: string;\n  image: string;\n  keywords: string;\n};\n\nexport type PageMeta = {\n  home: PageData;\n  stats: PageData;\n  utilities: PageData;\n  blogs: PageData;\n  bookmark: PageData;\n  certificates: PageData;\n  projects: PageData;\n  about: PageData;\n  privacy: PageData;\n  snippets: PageData;\n  books: PageData;\n  epigraphs: PageData;\n};\n\nexport type BookStatusId = 1 | 2 | 3;\n\nexport type HardcoverBook = {\n  id: number;\n  statusId: BookStatusId;\n  title: string;\n  subtitle: string | null;\n  slug: string;\n  pages: number | null;\n  releaseYear: number | null;\n  rating: number | null;\n  userRating: number | null;\n  coverUrl: string | null;\n  authors: string[];\n  updatedAt: string | null;\n  finishedAt: string | null;\n};\n\nexport type HardcoverProfile = {\n  username: string;\n  name: string;\n  booksCount: number;\n  currentYearGoal: {\n    target: number;\n    progress: number;\n    metric: string;\n    state: string;\n    year: number;\n  } | null;\n};\n\nexport type Snippet = {\n  slug: string;\n  title: string;\n  date: string;\n  excerpt: string;\n  image: string;\n};\n\nexport type MovieType = {\n  id: number;\n  name: string;\n  image: string;\n  url: string;\n  year: number;\n  watched: boolean;\n  rating: number;\n};\n"
  },
  {
    "path": "lib/windowsAnimation.ts",
    "content": "/* Adds a hover animation to an element by setting its background and border image properties. */\nexport function showHoverAnimation(e: any, isDarkMode: boolean) {\n  const rect = e.target.getBoundingClientRect();\n  const x = e.clientX - rect.left; // x position within the element.\n  const y = e.clientY - rect.top; //  y position within the element.\n\n  if (isDarkMode) {\n    e.target.style.background = `radial-gradient(circle at ${x}px ${y}px , rgba(255,255,255,0.2),rgba(255,255,255,0) )`;\n    e.target.style.borderImage = `radial-gradient(20% 75% at ${x}px ${y}px ,rgba(255,255,255,0.7),rgba(255,255,255,0.1) ) 1 / 1px / 0px stretch `;\n  }\n}\n\n/* Removes the hover animation from an element by setting its background and border image properties to null. */\nexport function removeHoverAnimation(e: any) {\n  e.target.style.background = null;\n  e.target.style.borderImage = null;\n}\n"
  },
  {
    "path": "next-env.d.ts",
    "content": "/// <reference types=\"next\" />\n/// <reference types=\"next/image-types/global\" />\n\n// NOTE: This file should not be edited\n// see https://nextjs.org/docs/basic-features/typescript for more information.\n"
  },
  {
    "path": "next.config.js",
    "content": "/**\n * @type {import('next').NextConfig}\n */\n\nconst withPWA = require(\"next-pwa\")({\n  dest: \"public\",\n  register: true,\n  skipWaiting: true,\n  disable: process.env.NODE_ENV === \"development\",\n  publicExcludes: [\"!resume.pdf\"],\n});\n\nmodule.exports = withPWA({\n  reactStrictMode: true,\n  images: {\n    domains: [\n      \"ucarecdn.com\",\n      \"cdn.buymeacoffee.com\",\n      \"res.cloudinary.com\",\n      \"imgur.com\",\n      \"i.imgur.com\",\n      \"cutt.ly\",\n      \"activity-graph.herokuapp.com\",\n      \"i.scdn.co\", // images from spotify\n      \"images.unsplash.com\",\n      \"m.media-amazon.com\", // for imdb images\n      \"cdn.sanity.io\", // sanity images\n      \"image.tmdb.org\", // tmdb images\n      \"scontent.cdninstagram.com\", // instagram media\n      \"assets.hardcover.app\",\n    ],\n  },\n  typescript: {\n    ignoreBuildErrors: false,\n  },\n});\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"portfolio-next\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"license\": \"MIT\",\n  \"scripts\": {\n    \"dev\": \"next dev\",\n    \"tsc\": \"tsc\",\n    \"build\": \"next build\",\n    \"start\": \"next start\",\n    \"tsc-watch\": \"tsc --watch\",\n    \"lint\": \"next lint\",\n    \"find:unused\": \"next-unused\"\n  },\n  \"dependencies\": {\n    \"@emailjs/browser\": \"^3.6.2\",\n    \"@google-analytics/data\": \"^3.1.2\",\n    \"@portabletext/react\": \"^3.0.4\",\n    \"@portabletext/to-html\": \"^2.0.0\",\n    \"@sanity/cli\": \"^3.14.1\",\n    \"@sanity/client\": \"^6.1.3\",\n    \"@sanity/image-url\": \"^1.0.2\",\n    \"@sanity/types\": \"^3.12.2\",\n    \"@tailwindcss/line-clamp\": \"^0.4.0\",\n    \"classnames\": \"^2.3.2\",\n    \"framer-motion\": \"^6.3.3\",\n    \"globby\": \"^13.1.1\",\n    \"gray-matter\": \"^4.0.3\",\n    \"groq\": \"^3.12.2\",\n    \"moment\": \"^2.29.4\",\n    \"next\": \"^13.1.6\",\n    \"next-mdx-remote\": \"^6.0.0\",\n    \"next-pwa\": \"^5.6.0\",\n    \"next-seo\": \"^6.1.0\",\n    \"nextjs-google-analytics\": \"^1.2.0\",\n    \"nprogress\": \"^0.2.0\",\n    \"react\": \"^18.2.0\",\n    \"react-dom\": \"^18.2.0\",\n    \"react-github-calendar\": \"^4.0.0\",\n    \"react-icons\": \"^5.0.1\",\n    \"react-markdown\": \"^8.0.7\",\n    \"react-qr-code\": \"^2.0.7\",\n    \"react-ripples\": \"^2.2.1\",\n    \"react-share\": \"^4.4.0\",\n    \"react-toastify\": \"^9.0.1\",\n    \"react-toggle-dark-mode\": \"^1.1.1\",\n    \"reading-time\": \"^1.5.0\",\n    \"recharts\": \"^2.7.1\",\n    \"rehype-autolink-headings\": \"^6.1.1\",\n    \"rehype-pretty-code\": \"^0.3.1\",\n    \"rehype-slug\": \"^5.0.1\",\n    \"rss\": \"^1.2.2\",\n    \"shiki\": \"^0.10.1\",\n    \"swr\": \"^1.3.0\"\n  },\n  \"devDependencies\": {\n    \"@supabase/supabase-js\": \"^2.2.3\",\n    \"@tailwindcss/typography\": \"^0.5.8\",\n    \"@types/next-pwa\": \"^5.6.0\",\n    \"@types/nprogress\": \"^0.2.0\",\n    \"@types/rss\": \"^0.0.29\",\n    \"autoprefixer\": \"^10.4.13\",\n    \"eslint\": \"8.14.0\",\n    \"eslint-config-next\": \"^13.1.6\",\n    \"next-unused\": \"^0.0.6\",\n    \"postcss\": \"^8.4.20\",\n    \"tailwindcss\": \"^3.2.4\",\n    \"typescript\": \"^4.9.4\"\n  }\n}\n"
  },
  {
    "path": "pages/404.tsx",
    "content": "import PageNotFound from \"@components/PageNotFound\";\n\nexport default PageNotFound;\n"
  },
  {
    "path": "pages/_app.tsx",
    "content": "import \"@styles/globals.css\";\nimport \"react-toastify/dist/ReactToastify.css\";\nimport Layout from \"@layout/Layout\";\nimport { useEffect } from \"react\";\nimport { useRouter } from \"next/router\";\nimport NProgress from \"nprogress\";\nimport \"nprogress/nprogress.css\";\nimport { DarkModeProvider } from \"@context/darkModeContext\";\nimport { GoogleAnalytics } from \"nextjs-google-analytics\";\nimport { AppProps } from \"next/app\";\n\nNProgress.configure({\n  easing: \"ease\",\n  speed: 800,\n  showSpinner: false,\n});\n\nfunction MyApp({ Component, pageProps }: AppProps) {\n  const router = useRouter();\n\n  useEffect(() => {\n    const start = () => {\n      NProgress.start();\n    };\n    const end = () => {\n      NProgress.done();\n    };\n    router.events.on(\"routeChangeStart\", start);\n    router.events.on(\"routeChangeComplete\", end);\n    router.events.on(\"routeChangeError\", end);\n    return () => {\n      router.events.off(\"routeChangeStart\", start);\n      router.events.off(\"routeChangeComplete\", end);\n      router.events.off(\"routeChangeError\", end);\n    };\n  }, [router.events]);\n\n  return (\n    <DarkModeProvider>\n      <Layout>\n        {process.env.NODE_ENV === \"production\" && (\n          <GoogleAnalytics strategy=\"lazyOnload\" />\n        )}\n        <Component {...pageProps} />\n      </Layout>\n    </DarkModeProvider>\n  );\n}\n\nexport default MyApp;\n"
  },
  {
    "path": "pages/_document.tsx",
    "content": "import { Html, Head, Main, NextScript } from \"next/document\";\n\nexport default function Document() {\n  return (\n    <Html lang=\"en\">\n      <Head>\n        {/* Barlow */}\n        <link\n          rel=\"preload\"\n          href=\"/fonts/Barlow/Barlow-400.woff2\"\n          as=\"font\"\n          type=\"font/woff2\"\n          crossOrigin=\"anonymous\"\n        />\n        <link\n          rel=\"preload\"\n          href=\"/fonts/Barlow/Barlow-500.woff2\"\n          as=\"font\"\n          type=\"font/woff2\"\n          crossOrigin=\"anonymous\"\n        />\n        <link\n          rel=\"preload\"\n          href=\"/fonts/Barlow/Barlow-600.woff2\"\n          as=\"font\"\n          type=\"font/woff2\"\n          crossOrigin=\"anonymous\"\n        />\n        <link\n          rel=\"preload\"\n          href=\"/fonts/Barlow/Barlow-700.woff2\"\n          as=\"font\"\n          type=\"font/woff2\"\n          crossOrigin=\"anonymous\"\n        />\n        <link\n          rel=\"preload\"\n          href=\"/fonts/Barlow/Barlow-800.woff2\"\n          as=\"font\"\n          type=\"font/woff2\"\n          crossOrigin=\"anonymous\"\n        />\n\n        {/* Inter */}\n        <link\n          rel=\"preload\"\n          href=\"/fonts/Inter-var.woff2\"\n          as=\"font\"\n          type=\"font/woff2\"\n          crossOrigin=\"anonymous\"\n        />\n        {/* Sarina */}\n        <link\n          rel=\"preload\"\n          href=\"/fonts/Sarina/Sarina-400.woff2\"\n          as=\"font\"\n          type=\"font/woff2\"\n          crossOrigin=\"anonymous\"\n        />\n      </Head>\n      <body>\n        <Main />\n        <NextScript />\n      </body>\n    </Html>\n  );\n}\n"
  },
  {
    "path": "pages/about.tsx",
    "content": "import { ILinkedinResponse, ITMDBData } from \"@lib/interface\";\nimport { IStaticPage } from \"@lib/interface/sanity\";\nimport Image from \"next/image\";\nimport MovieCard from \"@components/MovieCard\";\nimport MetaData from \"@components/MetaData\";\nimport MDXComponents from \"@components/MDXComponents\";\nimport PageHeader from \"@components/PageHeader\";\nimport { MDXRemote } from \"next-mdx-remote\";\nimport { fetchTMDBData } from \"@lib/tmdb\";\nimport { getStaticPageFromSlug } from \"@lib/sanityContent\";\nimport { getUserDataValue } from \"@lib/supabase\";\nimport { months } from \"@utils/date\";\nimport { motion } from \"framer-motion\";\nimport pageMeta from \"@content/meta\";\nimport { TIME_IN_SECONDS } from \"@utils/utils\";\n\nconst containerVariants = {\n  hidden: {},\n  visible: { transition: { staggerChildren: 0.08 } },\n};\n\nconst itemVariants = {\n  hidden: { opacity: 0, y: 12 },\n  visible: {\n    opacity: 1,\n    y: 0,\n    transition: { type: \"spring\" as const, stiffness: 160, damping: 22 },\n  },\n};\n\nexport default function About({\n  about,\n  movies,\n  linkedin,\n}: {\n  about: IStaticPage;\n  movies: ITMDBData[];\n  linkedin: string;\n}) {\n  const parsedLinkedIn: ILinkedinResponse = JSON.parse(linkedin);\n  return (\n    <>\n      <MetaData\n        title={pageMeta.about.title}\n        description={pageMeta.about.description}\n        previewImage={pageMeta.about.image}\n        keywords={pageMeta.about.keywords}\n      />\n\n      <PageHeader\n        watermark=\"/about\"\n        eyebrow=\"Profile — 001\"\n        title={about.title}\n        description={about.excerpt}\n        className=\"pb-16\"\n      >\n        {/* Prose content */}\n        <motion.div\n          initial={{ opacity: 0, y: 16 }}\n          animate={{ opacity: 1, y: 0 }}\n          transition={{ duration: 0.5, delay: 0.2 }}\n          className=\"prose-typography max-w-max\"\n        >\n          <MDXRemote\n            {...about.content}\n            frontmatter={{\n              slug: about.slug.current,\n              excerpt: about.excerpt,\n              title: about.title,\n              date: about.publishedAt,\n              keywords: about.keywords,\n              image: about.mainImage.asset.url,\n            }}\n            components={MDXComponents}\n          />\n        </motion.div>\n      </PageHeader>\n\n      {/* ── Experience ── */}\n      <div className=\"relative w-full px-6 sm:px-8 lg:px-12 pb-20 print:hidden\">\n        <div className=\"max-w-7xl mx-auto\">\n          {/* Section header */}\n          <div className=\"flex items-center gap-4 mb-10\">\n            <motion.div\n              initial={{ opacity: 0, x: -16 }}\n              whileInView={{ opacity: 1, x: 0 }}\n              viewport={{ once: true }}\n              className=\"flex items-center gap-3\"\n            >\n              <div className=\"h-px w-5 bg-gray-400 dark:bg-gray-600 flex-shrink-0\" />\n              <span className=\"font-mono text-[10px] tracking-[0.45em] uppercase text-gray-500 dark:text-gray-500\">\n                Career\n              </span>\n            </motion.div>\n            <motion.h2\n              initial={{ opacity: 0, y: 16 }}\n              whileInView={{ opacity: 1, y: 0 }}\n              viewport={{ once: true }}\n              transition={{ delay: 0.08 }}\n              className=\"text-2xl sm:text-3xl font-bold text-gray-900 dark:text-white\"\n            >\n              Recent Experience\n            </motion.h2>\n            <div className=\"h-px flex-1 bg-gray-200 dark:bg-neutral-700 hidden sm:block\" />\n          </div>\n\n          {/* Experience cards */}\n          <motion.div\n            variants={containerVariants}\n            initial=\"hidden\"\n            whileInView=\"visible\"\n            viewport={{ once: true, margin: \"-60px\" }}\n            className=\"flex flex-col gap-px bg-gray-200 dark:bg-neutral-700 border border-gray-200 dark:border-neutral-700\"\n          >\n            {parsedLinkedIn.experiences.map((experience) => (\n              <motion.div\n                key={experience.company_linkedin_profile_url}\n                variants={itemVariants}\n                className=\"bg-white dark:bg-darkPrimary p-6 flex gap-5 flex-col xs:flex-row\"\n              >\n                {/* Logo */}\n                <div className=\"flex-shrink-0 w-12 h-12 border border-gray-200 dark:border-gray-700 overflow-hidden flex items-center justify-center bg-white\">\n                  <Image\n                    src={experience.logo_url}\n                    width={48}\n                    height={48}\n                    className=\"object-contain w-full h-full\"\n                    alt={experience.company}\n                  />\n                </div>\n\n                {/* Content */}\n                <div\n                  className={`flex flex-col gap-3 flex-grow ${\n                    experience.job_titles.length > 1 ? \"ml-8\" : \"\"\n                  }`}\n                >\n                  {experience.job_titles.length > 1 && (\n                    <h2 className=\"text-base font-bold text-gray-900 dark:text-white -ml-8 font-mono uppercase tracking-wider\">\n                      {experience.company}\n                    </h2>\n                  )}\n\n                  {experience.job_titles.map((job, i) => (\n                    <div key={job.title} className=\"relative w-full\">\n                      {experience.job_titles.length > 1 && (\n                        <span\n                          className={`absolute -left-[25px] w-px bg-gray-300 dark:bg-gray-700 ${\n                            i === experience.job_titles.length - 1\n                              ? \"h-3 top-2\"\n                              : \"h-full top-2\"\n                          }`}\n                        />\n                      )}\n                      {experience.job_titles.length > 1 && (\n                        <span className=\"absolute -left-[28.5px] top-[9px] h-1.5 w-1.5 rounded-full bg-gray-400 dark:bg-gray-600 ring-2 ring-white dark:ring-darkPrimary\" />\n                      )}\n\n                      <div className=\"flex flex-col sm:flex-row sm:items-start sm:justify-between gap-1\">\n                        <div>\n                          <h3 className=\"text-base font-semibold text-gray-900 dark:text-white\">\n                            {job.title}\n                          </h3>\n                          {experience.job_titles.length === 1 && (\n                            <p className=\"text-sm text-gray-600 dark:text-gray-400\">\n                              {experience.company}\n                            </p>\n                          )}\n                          {job.location && (\n                            <p className=\"text-xs font-mono text-gray-500 dark:text-gray-500 mt-0.5\">\n                              {job.location}\n                            </p>\n                          )}\n                        </div>\n                        <p className=\"text-xs font-mono text-gray-500 dark:text-gray-500 whitespace-nowrap sm:text-right flex-shrink-0\">\n                          {months[job.starts_at.month - 1]} {job.starts_at.year}\n                          {\" — \"}\n                          {!job.ends_at\n                            ? \"Present\"\n                            : `${months[job.ends_at.month - 1]} ${job.ends_at.year}`}\n                        </p>\n                      </div>\n\n                      {job.description && (\n                        <p className=\"mt-2 text-sm text-gray-600 dark:text-gray-400 leading-relaxed whitespace-pre-wrap border-l-2 border-gray-200 dark:border-gray-700 pl-3\">\n                          {job.description}\n                        </p>\n                      )}\n                    </div>\n                  ))}\n                </div>\n              </motion.div>\n            ))}\n          </motion.div>\n        </div>\n      </div>\n\n      {/* ── Movies & TV ── */}\n      <div className=\"relative w-full px-6 sm:px-8 lg:px-12 pb-24 print:hidden overflow-hidden\">\n        <div className=\"max-w-7xl mx-auto\">\n          {/* Section header */}\n          <div className=\"flex items-center gap-4 mb-10\">\n            <motion.div\n              initial={{ opacity: 0, x: -16 }}\n              whileInView={{ opacity: 1, x: 0 }}\n              viewport={{ once: true }}\n              className=\"flex items-center gap-3\"\n            >\n              <div className=\"h-px w-5 bg-gray-400 dark:bg-gray-600 flex-shrink-0\" />\n              <span className=\"font-mono text-[10px] tracking-[0.45em] uppercase text-gray-500 dark:text-gray-500\">\n                Watching\n              </span>\n            </motion.div>\n            <motion.h2\n              initial={{ opacity: 0, y: 16 }}\n              whileInView={{ opacity: 1, y: 0 }}\n              viewport={{ once: true }}\n              transition={{ delay: 0.08 }}\n              className=\"text-2xl sm:text-3xl font-bold text-gray-900 dark:text-white\"\n            >\n              Movies & TV Series\n            </motion.h2>\n            <div className=\"h-px flex-1 bg-gray-200 dark:bg-neutral-700 hidden sm:block\" />\n          </div>\n\n          {/* Horizontal scroll strip */}\n          <motion.div\n            variants={containerVariants}\n            initial=\"hidden\"\n            whileInView=\"visible\"\n            viewport={{ once: true, margin: \"-60px\" }}\n            className=\"flex items-stretch gap-3 overflow-x-auto pb-4 horizontal-scrollbar\"\n          >\n            {movies.length !== 0 ? (\n              movies.map((movie) => <MovieCard key={movie.id} movie={movie} />)\n            ) : (\n              <div className=\"flex items- w-full h-10 bg-gray-100 dark:bg-darkPrimary rounded-md\">\n                <p className=\"text-gray-500 dark:text-gray-400\">\n                  No recent activity.\n                </p>\n              </div>\n            )}\n          </motion.div>\n        </div>\n      </div>\n    </>\n  );\n}\n\nexport async function getStaticProps() {\n  const about = await getStaticPageFromSlug(\"about\");\n\n  const movies = await fetchTMDBData();\n  const { data: linkedin } = await getUserDataValue(\"linkedin\");\n\n  return {\n    props: {\n      about,\n      movies,\n      linkedin,\n    },\n    revalidate: TIME_IN_SECONDS.ONE_DAY, // Revalidate every 24 hours\n  };\n}\n"
  },
  {
    "path": "pages/api/books.ts",
    "content": "import { NextApiRequest, NextApiResponse } from \"next\";\nimport { getMyBooks } from \"../../lib/hardcover\";\nimport { HardcoverBook } from \"../../lib/types\";\n\nexport default async function handler(\n  _req: NextApiRequest,\n  res: NextApiResponse<{ books: HardcoverBook[] } | { error: string }>,\n) {\n  try {\n    const books = await getMyBooks();\n\n    // Revalidate once per day (86 400 s), serve stale for up to 12 h while revalidating\n    res.setHeader(\n      \"Cache-Control\",\n      \"public, s-maxage=86400, stale-while-revalidate=43200\",\n    );\n\n    return res.status(200).json({ books });\n  } catch (error) {\n    const message =\n      error instanceof Error ? error.message : \"Failed to fetch books\";\n    return res.status(500).json({ error: message });\n  }\n}\n"
  },
  {
    "path": "pages/api/ga.ts",
    "content": "import { NextApiRequest, NextApiResponse } from \"next\";\nimport { BetaAnalyticsDataClient } from \"@google-analytics/data\";\n\nconst propertyId = process.env.GA_PROPERTY_ID;\nconst DAYS = 7;\n\nconst analyticsDataClient = new BetaAnalyticsDataClient({\n  credentials: {\n    client_email: process.env.GA_CLIENT_EMAIL,\n    private_key: process.env.GA_PRIVATE_KEY?.replace(/\\n/gm, '\\n'),\n  },\n});\n\nexport default async function handler(\n  _req: NextApiRequest,\n  res: NextApiResponse\n) {\n  const [response] = await analyticsDataClient.runReport({\n    property: `properties/${propertyId}`,\n    dateRanges: [\n      {\n        startDate: `${DAYS}daysAgo`,\n        endDate: \"today\",\n      },\n    ],\n    dimensions: [\n      {\n        name: \"year\",\n      },\n    ],\n    metrics: [\n      {\n        name: \"activeUsers\",\n      },\n    ],\n  });\n\n  let totalVisitors = 0;\n  response.rows?.forEach((row: any) => {\n    totalVisitors += parseInt(row.metricValues[0].value);\n  });\n\n  res.setHeader(\n    \"Cache-Control\",\n    \"public, s-maxage=43200, stale-while-revalidate=21600\"\n  );\n\n  return res.status(200).json({\n    totalVisitors,\n    days: 7,\n  });\n}\n"
  },
  {
    "path": "pages/api/now-playing.ts",
    "content": "import { NextApiRequest, NextApiResponse } from \"next\";\nimport { currentlyPlayingSong } from \"../../lib/spotify\";\n\nexport default async function handler(\n  _req: NextApiRequest,\n  res: NextApiResponse\n) {\n  const response = await currentlyPlayingSong();\n\n  if (response.status === 204 || response.status > 400) {\n    return res.status(200).json({ isPlaying: false });\n  }\n\n  const song = await response.json();\n\n  if (song.item === null) {\n    return res.status(200).json({ isPlaying: false });\n  }\n\n  const isPlaying = song.is_playing;\n  const title = song.item.name;\n  const artist = song.item.artists.map((artist: any) => artist.name).join(\", \");\n  const album = song.item.album.name;\n  const albumImageUrl = song.item.album.images[0].url;\n  const songUrl = song.item.external_urls.spotify;\n\n  // res.setHeader(\n  //   \"Cache-Control\",\n  //   \"public, s-maxage=60, stale-while-revalidate=10\"\n  // );\n\n  return res.status(200).json({\n    album,\n    albumImageUrl,\n    artist,\n    isPlaying,\n    songUrl,\n    title,\n  });\n}\n"
  },
  {
    "path": "pages/api/posts/insta.ts",
    "content": "import { NextApiRequest, NextApiResponse } from \"next\";\n\nimport { getInstagramPosts } from \"@lib/instaposts\";\n\nexport default async function handler(\n  req: NextApiRequest,\n  res: NextApiResponse\n) {\n  const paginationCode = req.query?.code ?? null;\n  const mode = req.query?.mode ?? null;\n\n  const additionalParams = mode\n    ? mode === \"before\"\n      ? { before: paginationCode }\n      : { after: paginationCode }\n    : {};\n\n  const outputData = await getInstagramPosts(additionalParams);\n\n  res.setHeader(\n    \"Cache-Control\",\n    \"public, s-maxage=3600, stale-while-revalidate=1800\"\n  );\n\n  return res.status(200).json(outputData);\n}\n"
  },
  {
    "path": "pages/api/revalidate.ts",
    "content": "import type { NextApiRequest, NextApiResponse } from \"next\";\n\ninterface ExtendedNextApiRequest extends NextApiRequest {\n  query: {\n    revalidateUrl: string;\n    secret: string;\n  };\n}\nexport default async function handler(\n  req: ExtendedNextApiRequest,\n  res: NextApiResponse,\n) {\n  // Check for secret to confirm this is a valid request\n  if (req.query.secret !== process.env.REVALIDATE_SECRET) {\n    return res.status(401).json({\n      message:\n        \"Invalid token alert! It looks like you're trying to sneak in without proper authorization. Please present a valid token or face rejection\",\n    });\n  }\n\n  try {\n    // Regenerate the projects page\n    await res.revalidate(req.query.revalidateUrl);\n    return res.json({ revalidated: true });\n  } catch (err) {\n    // If there was an error, Next.js will continue\n    // to show the last successfully generated page\n    return res.status(500).send(\"Error revalidating\");\n  }\n}\n"
  },
  {
    "path": "pages/api/stats/artists.ts",
    "content": "import { NextApiRequest, NextApiResponse } from \"next\";\nimport { topArtists } from \"../../../lib/spotify\";\n\nexport default async function handler(\n  _req: NextApiRequest,\n  res: NextApiResponse\n) {\n  const items = await topArtists();\n\n  const artists = items.map((artist) => ({\n    id: artist.id,\n    name: artist.name,\n    url: artist.external_urls.spotify,\n    popularity: artist.popularity,\n    coverImage: artist.images ? artist.images[1] : null,\n  }));\n\n  res.setHeader(\n    \"Cache-Control\",\n    \"public, s-maxage=86400, stale-while-revalidate=43200\"\n  );\n\n  return res.status(200).json(artists);\n}\n"
  },
  {
    "path": "pages/api/stats/devto.ts",
    "content": "import { NextRequest, NextResponse } from \"next/server\";\n\nimport { getUserDataValue } from \"@lib/supabase\";\n\nexport const config = {\n  runtime: \"edge\", // this is a pre-requisite\n};\n\nexport default async function handler(_req: NextRequest) {\n  const { data } = await getUserDataValue(\"devto_stats\");\n\n  return NextResponse.json(JSON.parse(data), {\n    headers: {\n      \"Cache-Control\": \"public, s-maxage=86400, stale-while-revalidate=43200\",\n    },\n    status: 200,\n  });\n}\n\n/* \nResponse of this API request is: \n{\"followers\":18034,\"likes\":13603,\"views\":754641,\"comments\":958,\"posts\":119}\n*/\n"
  },
  {
    "path": "pages/api/stats/github-contribution.ts",
    "content": "import { NextApiRequest, NextApiResponse } from \"next\";\nimport { getGithubContribution } from \"../../../lib/github\";\n\nexport default async function handler(\n  _req: NextApiRequest,\n  res: NextApiResponse\n) {\n  const data = await getGithubContribution();\n\n  res.setHeader(\n    \"Cache-Control\",\n    \"public, s-maxage=86400, stale-while-revalidate=43200\"\n  );\n\n  return res.status(200).json(data);\n}\n"
  },
  {
    "path": "pages/api/stats/github.ts",
    "content": "import { NextApiRequest, NextApiResponse } from \"next\";\nimport { fetchGithub, getGithubStarsAndForks } from \"../../../lib/github\";\n\nexport default async function handler(\n  _req: NextApiRequest,\n  res: NextApiResponse\n) {\n  const {\n    public_repos: repos,\n    public_gists: gists,\n    followers,\n  } = await fetchGithub();\n  const { githubStars, forks } = await getGithubStarsAndForks();\n\n  res.setHeader(\n    \"Cache-Control\",\n    \"public, s-maxage=86400, stale-while-revalidate=43200\"\n  );\n\n  return res.status(200).json({\n    repos,\n    gists,\n    followers,\n    githubStars,\n    forks,\n  });\n}\n"
  },
  {
    "path": "pages/api/stats/monkeytype.ts",
    "content": "import { NextApiRequest, NextApiResponse } from \"next\";\n\nconst BASE_URL = \"https://api.monkeytype.com\";\n\nasync function monkeyFetch(endpoint: string) {\n  const res = await fetch(`${BASE_URL}${endpoint}`, {\n    headers: {\n      Authorization: `ApeKey ${process.env.MONKEYTYPE_APE_KEY}`,\n      Accept: \"application/json\",\n    },\n  });\n  if (!res.ok) return null;\n  const json = await res.json();\n  return json.data;\n}\n\nexport default async function handler(\n  _req: NextApiRequest,\n  res: NextApiResponse,\n) {\n  try {\n    const [pb15, pb30, pb60, stats, streak, results, rankData] =\n      await Promise.all([\n        monkeyFetch(\"/users/personalBests?mode=time&mode2=15\"),\n        monkeyFetch(\"/users/personalBests?mode=time&mode2=30\"),\n        monkeyFetch(\"/users/personalBests?mode=time&mode2=60\"),\n        monkeyFetch(\"/users/stats\"),\n        monkeyFetch(\"/users/streak\"),\n        monkeyFetch(\"/results?limit=50\"),\n        monkeyFetch(\"/leaderboards/rank?language=english&mode=time&mode2=60\"),\n      ]);\n\n    // personal bests come as arrays – pick the top entry (highest wpm)\n    const best = (arr: any[] | null) => {\n      if (!arr || arr.length === 0) return null;\n      return arr.reduce((a: any, b: any) => (a.wpm > b.wpm ? a : b));\n    };\n\n    const personalBests = {\n      time15: best(pb15),\n      time30: best(pb30),\n      time60: best(pb60),\n    };\n\n    // Process recent results\n    const recentResults: any[] = Array.isArray(results) ? results : [];\n\n    // WPM trend: last 30 results in chronological order\n    const trendResults = recentResults\n      .slice(0, 30)\n      .reverse()\n      .map((r: any, i: number) => ({\n        index: i + 1,\n        wpm: Math.round(r.wpm),\n        raw: Math.round(r.rawWpm ?? r.wpm),\n        acc: Math.round(r.acc * 100) / 100,\n        mode: r.mode2 ? `${r.mode} ${r.mode2}s` : r.mode,\n        timestamp: r.timestamp,\n      }));\n\n    const leaderboardRank: number | null = rankData?.rank ?? null;\n\n    res.setHeader(\n      \"Cache-Control\",\n      \"public, s-maxage=86400, stale-while-revalidate=43200\",\n    );\n\n    return res.status(200).json({\n      personalBests,\n      stats: stats ?? { completedTests: 0, startedTests: 0, timeTyping: 0 },\n      streak: streak ?? { length: 0, maxLength: 0 },\n      trendResults,\n      leaderboardRank,\n    });\n  } catch (error) {\n    console.error(\"MonkeyType API error:\", error);\n    return res.status(500).json({ error: \"Failed to fetch MonkeyType data\" });\n  }\n}\n"
  },
  {
    "path": "pages/api/stats/tracks.ts",
    "content": "import { NextApiRequest, NextApiResponse } from \"next\";\nimport { topTracks } from \"../../../lib/spotify\";\n\nexport default async function handler(\n  _req: NextApiRequest,\n  res: NextApiResponse\n) {\n  const items = await topTracks();\n\n  const tracks = items.map((track) => ({\n    title: track.name,\n    artist: track.artists.map((artist: any) => artist.name).join(\", \"),\n    url: track.external_urls.spotify,\n    coverImage: track.album.images[1],\n  }));\n\n  res.setHeader(\n    \"Cache-Control\",\n    \"public, s-maxage=86400, stale-while-revalidate=43200\"\n  );\n\n  return res.status(200).json(tracks);\n}\n"
  },
  {
    "path": "pages/api/validate/email.ts",
    "content": "import { IEmailValidation } from \"@lib/interface\";\nimport { NextApiRequest, NextApiResponse } from \"next\";\n\n// Function to check if the host is valid\nconst isValidHost = (host: string) => {\n  if (process.env.NODE_ENV === \"production\")\n    return host.toLowerCase() === \"j471n.in\";\n  return true;\n};\n\nexport default async function handler(\n  req: NextApiRequest,\n  res: NextApiResponse,\n) {\n  const origin = req.headers.origin;\n  const host = req.headers.host;\n\n  console.log({\n    origin,\n    host,\n  });\n\n  if (!isValidHost(host!)) {\n    return res.status(400).json({\n      status: \"error\",\n      message: \"You are unauthorize to access this route.\",\n    });\n  }\n\n  if (req.method !== \"POST\") {\n    return res.status(400).json({\n      status: \"error\",\n      message: \"Invalid Method, use POST\",\n    });\n  }\n\n  const { email } = JSON.parse(req.body);\n\n  const url = \"https://mailcheck.p.rapidapi.com/?domain=\" + email;\n  const options = {\n    method: \"GET\",\n    headers: {\n      \"X-RapidAPI-Key\": process.env.EMAIL_VALIDATION_API!,\n      \"X-RapidAPI-Host\": \"mailcheck.p.rapidapi.com\",\n    },\n  };\n\n  const response = await fetch(url, options);\n\n  if (response.status !== 200) {\n    return res.status(400).json({\n      status: \"error\",\n      message:\n        \"Unable to process your request right now. Please try again later.\",\n    });\n  }\n\n  const { valid, disposable } = (await response.json()) as IEmailValidation;\n\n  return res.status(200).json({\n    status: \"success\",\n    message: \"Email is deliverable.\",\n    valid: valid && !disposable,\n  });\n}\n"
  },
  {
    "path": "pages/api/views/[slug].ts",
    "content": "import { addView, getViewBySlug } from \"@lib/supabase\";\nimport { NextApiRequest, NextApiResponse } from \"next\";\n\n/* Extending API request because by default quey.slug return string | string[] and I only want string */\ninterface ExtendedNextApiRequest extends NextApiRequest {\n  query: {\n    slug: string;\n  };\n}\n\n/**\n * This function handles HTTP requests made to a specific route. It takes in a \"slug\" parameter from the request query.\n *\n * If the request method is GET, it calls the getViewBySlug(slug) function, which retrieves the view count of the specified blog post by its slug value.\n * If the view count is not found, it sends a response with a status code of 404 and a JSON object with a message of \"Slug not found\"\n * If the view count is found, it sends a response with a status code of 200 and the data in JSON format.\n *\n * If the request method is POST and the app is running in production environment, it calls the addView(slug) function,\n * which adds a view to the specified blog post. It sends a response with the status code and data returned from the addView function.\n *\n * If the request method is POST and the app is running in development environment, it sends a response with a status code of 401 and a JSON object with a message of \"In Development, Can't add views\"\n */\n\nexport default async function viewsSlug(\n  req: ExtendedNextApiRequest,\n  res: NextApiResponse\n) {\n  const slug = req.query.slug;\n  if (req.method === \"GET\") {\n    const data = await getViewBySlug(slug);\n    if (data === undefined) {\n      return res\n        .status(404)\n        .json({\n          message:\n            \"Sorry, the slug you're looking for has gone for a coffee break. Please try again later or make a cup of tea while you wait.\",\n        });\n    } else {\n      return res.status(200).json(data);\n    }\n  }\n  // check if the app in the production and req method is post only then add the view to the database\n  if (req.method === \"POST\" && process.env.NODE_ENV === \"production\") {\n    const supabaseResponse = await addView(slug);\n    res.status(supabaseResponse?.status!).json(supabaseResponse);\n  } else {\n    return res.status(401).json({\n      message: \"In Development, Can't add views\",\n    });\n  }\n}\n"
  },
  {
    "path": "pages/api/views/index.ts",
    "content": "import { NextRequest, NextResponse } from \"next/server\";\n\nimport { getAllViews } from \"@lib/supabase\";\n\nexport const config = {\n  runtime: \"edge\", // this is a pre-requisite\n};\n\n/**\n * This function handles HTTP requests made to a specific route.\n * If the request method is GET, it calls the getAllViews() function, which retrieves all views count and all blog post from the database\n * and sends the result as a JSON object in the response with a status code of 200.\n * If the request method is not GET, it sends a response with a status code of 405 and a JSON object with a message of \"Invalid method use GET\"\n */\nexport default async function views(req: NextRequest) {\n  if (req.method === \"GET\") {\n    return NextResponse.json(await getAllViews(), {\n      status: 200,\n    });\n  } else {\n    return NextResponse.json(\n      {\n        error:\n          \"Invalid method detected! Please switch to GET before proceeding. Trust me, it's the way to go\",\n      },\n      {\n        status: 405,\n      }\n    );\n  }\n}\n"
  },
  {
    "path": "pages/blogs/[slug].tsx",
    "content": "import { getAllSlugs, getPostFromSlug } from \"@lib/sanityContent\";\n\nimport BlogLayout from \"@layout/BlogLayout\";\nimport { BlogPost } from \"@lib/interface/sanity\";\nimport { GetStaticPropsContext } from \"next\";\nimport MDXComponents from \"@components/MDXComponents\";\nimport { MDXRemote } from \"next-mdx-remote\";\nimport Metadata from \"@components/MetaData\";\nimport PageNotFound from \"@components/PageNotFound\";\nimport { useEffect } from \"react\";\nimport { TIME_IN_SECONDS } from \"@utils/utils\";\n\nexport default function Post({\n  post,\n  error,\n}: {\n  post: BlogPost;\n  error: boolean;\n}) {\n  // Adding Views to the supabase database\n  useEffect(() => {\n    const registerView = () =>\n      fetch(`/api/views/${post.slug.current}`, {\n        method: \"POST\",\n      });\n\n    post != null && registerView();\n  }, [post]);\n\n  if (error) return <PageNotFound />;\n\n  return (\n    <>\n      <Metadata\n        title={post.title}\n        suffix=\"Jatin Sharma\"\n        description={post.excerpt}\n        previewImage={post.mainImage.asset.url}\n        keywords={post.keywords ?? \"\"}\n      />\n\n      <BlogLayout post={post}>\n        <MDXRemote\n          {...post.content}\n          frontmatter={{\n            slug: post.slug.current,\n            excerpt: post.excerpt,\n            title: post.title,\n            date: post.publishedAt,\n            keywords: post.keywords ?? \"\",\n            image: post.mainImage.asset.url,\n          }}\n          components={MDXComponents}\n        />\n      </BlogLayout>\n    </>\n  );\n}\n\ntype StaticProps = GetStaticPropsContext & {\n  params: {\n    slug: string;\n  };\n};\n\nexport async function getStaticProps({ params }: StaticProps) {\n  const { slug } = params;\n  const post = await getPostFromSlug(slug);\n\n  if (post != null) {\n    return {\n      props: {\n        error: false,\n        post,\n      },\n      revalidate: TIME_IN_SECONDS.SIX_HOURS,\n    };\n  } else {\n    return {\n      props: {\n        error: true,\n        post: null,\n      },\n      revalidate: TIME_IN_SECONDS.SIX_HOURS,\n    };\n  }\n}\n\nexport async function getStaticPaths() {\n  const slugs = await getAllSlugs({\n    type: \"post\",\n  });\n  const paths = slugs.map((slug: any) => ({ params: { slug } }));\n\n  return {\n    paths,\n    fallback: \"blocking\",\n  };\n}\n"
  },
  {
    "path": "pages/blogs/bookmark.tsx",
    "content": "import { motion } from \"framer-motion\";\nimport Blog from \"@components/Blog\";\nimport Metadata from \"@components/MetaData\";\nimport PageHeader from \"@components/PageHeader\";\nimport useBookmarkBlogs from \"@hooks/useBookmarkBlogs\";\nimport pageMeta from \"@content/meta\";\n\nconst containerVariants = {\n  hidden: {},\n  visible: { transition: { staggerChildren: 0.04 } },\n};\n\nexport default function Blogs() {\n  const { bookmarkedBlogs } = useBookmarkBlogs(\"blogs\", []);\n\n  return (\n    <>\n      <Metadata\n        title={pageMeta.bookmark.title}\n        description={pageMeta.bookmark.description}\n        previewImage={pageMeta.bookmark.image}\n        keywords={pageMeta.bookmark.keywords}\n      />\n\n      <PageHeader\n        watermark=\"saved\"\n        eyebrow=\"Bookmarks\"\n        title=\"Reading List\"\n        description=\"Articles you've saved to read later. Stored locally in your browser.\"\n        className=\"pb-24\"\n      >\n        {bookmarkedBlogs?.length > 0 ? (\n          <motion.div\n            variants={containerVariants}\n            initial=\"hidden\"\n            animate=\"visible\"\n            className=\"border border-gray-200 dark:border-neutral-700\"\n          >\n            {bookmarkedBlogs.map((blog, index) => (\n              <Blog key={index} blog={blog} index={index} />\n            ))}\n          </motion.div>\n        ) : (\n          <div className=\"py-16 text-center font-mono text-[11px] tracking-[0.4em] uppercase text-gray-400 dark:text-gray-600\">\n            No bookmarks yet\n          </div>\n        )}\n      </PageHeader>\n    </>\n  );\n}\n"
  },
  {
    "path": "pages/blogs/index.tsx",
    "content": "import { AnimatePresence, motion } from \"framer-motion\";\nimport React, { useEffect, useRef, useState } from \"react\";\n\nimport { BiRss } from \"react-icons/bi\";\nimport Blog from \"@components/Blog\";\nimport { BlogPost } from \"@lib/interface/sanity\";\nimport { CgSearch } from \"react-icons/cg\";\nimport Link from \"next/link\";\nimport Metadata from \"@components/MetaData\";\nimport PageHeader from \"@components/PageHeader\";\nimport { debounce } from \"@utils/functions\";\nimport { getAllPostsMeta } from \"@lib/sanityContent\";\nimport pageMeta from \"@content/meta\";\nimport { TIME_IN_SECONDS } from \"@utils/utils\";\n\nconst containerVariants = {\n  hidden: {},\n  visible: { transition: { staggerChildren: 0.04 } },\n};\n\nexport default function Blogs({ blogs }: { blogs: BlogPost[] }) {\n  const [filteredBlogs, setFilteredBlogs] = useState([...blogs]);\n  const [query, setQuery] = useState(\"\");\n  const searchRef = useRef<HTMLInputElement>(null!);\n\n  const handleSearch = debounce((value: string) => {\n    setQuery(value);\n    setFilteredBlogs(\n      blogs.filter((post: BlogPost) =>\n        post.title.toLowerCase().includes(value.trim().toLowerCase()),\n      ),\n    );\n  }, 300);\n\n  function handleAutoSearch(e: KeyboardEvent) {\n    if (e.code === \"Slash\" && e.ctrlKey) {\n      searchRef.current?.focus();\n    }\n  }\n\n  useEffect(() => {\n    document.addEventListener(\"keydown\", handleAutoSearch);\n    return () => document.removeEventListener(\"keydown\", handleAutoSearch);\n  }, []);\n\n  return (\n    <>\n      <Metadata\n        title={pageMeta.blogs.title}\n        description={pageMeta.blogs.description}\n        previewImage={pageMeta.blogs.image}\n        keywords={pageMeta.blogs.keywords}\n      />\n\n      <PageHeader\n        watermark=\"blog\"\n        eyebrow=\"Writing — 001\"\n        title=\"Blog\"\n        description={`Writing about web development and tech since 2021. ${blogs.length} articles published.`}\n        className=\"pb-24\"\n      >\n        {/* Search bar */}\n        <div className=\"relative mb-8 max-w-xl\">\n          <CgSearch className=\"absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400 dark:text-gray-600 pointer-events-none\" />\n          <input\n            ref={searchRef}\n            type=\"text\"\n            onChange={(e) => handleSearch(e.target.value)}\n            placeholder=\"Search articles… (Ctrl + /)\"\n            className=\"w-full pl-9 pr-4 py-2.5 text-sm bg-white dark:bg-darkSecondary border border-gray-200 dark:border-neutral-700 text-gray-900 dark:text-white placeholder:text-gray-400 dark:placeholder:text-gray-600 outline-none focus:border-gray-400 dark:focus:border-gray-600 transition-colors font-mono\"\n          />\n        </div>\n\n        {/* Results header */}\n        <div className=\"flex items-center justify-between mb-4\">\n          <span className=\"font-mono text-[10px] tracking-[0.45em] uppercase text-gray-500 dark:text-gray-500\">\n            {query\n              ? `${filteredBlogs.length} result${filteredBlogs.length !== 1 ? \"s\" : \"\"} for \"${query}\"`\n              : `${blogs.length} articles`}\n          </span>\n          <Link\n            href=\"/rss\"\n            title=\"RSS Feed\"\n            className=\"flex items-center gap-1.5 font-mono text-[10px] tracking-[0.3em] uppercase text-gray-400 dark:text-gray-600 hover:text-gray-900 dark:hover:text-white transition-colors\"\n          >\n            <BiRss className=\"w-3.5 h-3.5\" />\n            RSS\n          </Link>\n        </div>\n\n        {/* Blog list */}\n        <AnimatePresence exitBeforeEnter>\n          {filteredBlogs.length > 0 ? (\n            <motion.div\n              key=\"results\"\n              variants={containerVariants}\n              initial=\"hidden\"\n              animate=\"visible\"\n              className=\"border border-gray-200 dark:border-neutral-700\"\n            >\n              {filteredBlogs.map((blog, index) => (\n                <Blog key={blog.slug.current} blog={blog} index={index} />\n              ))}\n            </motion.div>\n          ) : (\n            <motion.p\n              key=\"empty\"\n              initial={{ opacity: 0 }}\n              animate={{ opacity: 1 }}\n              exit={{ opacity: 0 }}\n              className=\"py-16 text-center font-mono text-[11px] tracking-[0.4em] uppercase text-gray-400 dark:text-gray-600\"\n            >\n              No results found\n            </motion.p>\n          )}\n        </AnimatePresence>\n      </PageHeader>\n    </>\n  );\n}\n\nexport async function getStaticProps() {\n  const results = await getAllPostsMeta();\n  return {\n    props: { blogs: results },\n    revalidate: TIME_IN_SECONDS.TEN_MINUTES,\n  };\n}\n"
  },
  {
    "path": "pages/books.tsx",
    "content": "import React, { useState } from \"react\";\nimport { motion, AnimatePresence } from \"framer-motion\";\nimport { CgSearch } from \"react-icons/cg\";\nimport MetaData from \"@components/MetaData\";\nimport PageHeader from \"@components/PageHeader\";\nimport BookCard from \"@components/BookCard\";\nimport CreateAnIssue from \"@components/CreateAnIssue\";\nimport pageMeta from \"@content/meta\";\nimport { getMyBooks, getMyProfile } from \"@lib/hardcover\";\nimport { HardcoverBook, HardcoverProfile } from \"@lib/types\";\nimport { debounce } from \"@utils/functions\";\nimport { TIME_IN_SECONDS } from \"@utils/utils\";\n\nconst TABS = [\n  { id: 3, label: \"Read\" },\n  { id: 2, label: \"Reading\" },\n  { id: 1, label: \"Want to Read\" },\n] as const;\n\ntype TabId = (typeof TABS)[number][\"id\"];\n\nconst containerVariants = {\n  hidden: {},\n  visible: { transition: { staggerChildren: 0.05 } },\n};\n\nconst fadeSlide = {\n  hidden: { opacity: 0, y: 8 },\n  visible: { opacity: 1, y: 0, transition: { duration: 0.25 } },\n  exit: { opacity: 0, y: -8, transition: { duration: 0.15 } },\n};\n\nconst statItem = {\n  hidden: { opacity: 0, y: 16 },\n  visible: {\n    opacity: 1,\n    y: 0,\n    transition: { type: \"spring\" as const, stiffness: 220, damping: 24 },\n  },\n};\n\n/* ── Stats section ── */\nfunction BooksStats({\n  books,\n  profile,\n}: {\n  books: HardcoverBook[];\n  profile: HardcoverProfile;\n}) {\n  const readCount = books.filter((b) => b.statusId === 3).length;\n  const readingCount = books.filter((b) => b.statusId === 2).length;\n  const wantCount = books.filter((b) => b.statusId === 1).length;\n  const pagesRead = books\n    .filter((b) => b.statusId === 3)\n    .reduce((sum, b) => sum + (b.pages ?? 0), 0);\n\n  const stats = [\n    { label: \"Books Read\", value: readCount.toString() },\n    { label: \"Currently Reading\", value: readingCount.toString() },\n    { label: \"Want to Read\", value: wantCount.toString() },\n    {\n      label: \"Pages Read\",\n      value: pagesRead > 0 ? pagesRead.toLocaleString() : \"—\",\n    },\n  ];\n\n  const goal = profile.currentYearGoal;\n  const goalPct = goal\n    ? Math.min(100, Math.round((goal.progress / goal.target) * 100))\n    : 0;\n\n  return (\n    <div className=\"mb-12 space-y-6\">\n      {/* Eyebrow */}\n      <motion.div\n        initial={{ opacity: 0, x: -16 }}\n        whileInView={{ opacity: 1, x: 0 }}\n        viewport={{ once: true }}\n        className=\"flex items-center gap-3\"\n      >\n        <div className=\"h-px w-5 bg-gray-400 dark:bg-neutral-600 flex-shrink-0\" />\n        <span className=\"font-mono text-[9px] tracking-[0.45em] uppercase text-gray-500 dark:text-gray-500\">\n          Reading Stats\n        </span>\n      </motion.div>\n\n      {/* Stat cards */}\n      <motion.div\n        variants={containerVariants}\n        initial=\"hidden\"\n        whileInView=\"visible\"\n        viewport={{ once: true, margin: \"-20px\" }}\n        className=\"grid grid-cols-2 sm:grid-cols-4 gap-px bg-gray-200 dark:bg-neutral-700 border border-gray-200 dark:border-neutral-700\"\n      >\n        {stats.map((s) => (\n          <motion.div\n            key={s.label}\n            variants={statItem}\n            className=\"flex flex-col justify-center p-5 bg-white dark:bg-darkPrimary\"\n          >\n            <p className=\"text-[9px] font-mono tracking-[0.4em] uppercase text-gray-500 dark:text-gray-500 mb-2\">\n              {s.label}\n            </p>\n            <p className=\"text-3xl font-black text-gray-900 dark:text-white leading-none tabular-nums\">\n              {s.value}\n            </p>\n          </motion.div>\n        ))}\n      </motion.div>\n\n      {/* Reading goal */}\n      {goal && (\n        <motion.div\n          initial={{ opacity: 0, y: 12 }}\n          whileInView={{ opacity: 1, y: 0 }}\n          viewport={{ once: true }}\n          transition={{ delay: 0.15 }}\n          className=\"border border-gray-200 dark:border-neutral-700 bg-white dark:bg-darkPrimary p-5\"\n        >\n          <div className=\"flex items-end justify-between mb-3\">\n            <div>\n              <p className=\"text-[9px] font-mono tracking-[0.4em] uppercase text-gray-500 dark:text-gray-500 mb-1\">\n                {goal.year} Reading Goal\n              </p>\n              <p className=\"text-sm font-semibold text-gray-900 dark:text-white\">\n                {goal.progress}{\" \"}\n                <span className=\"text-gray-400 dark:text-gray-500 font-normal\">\n                  / {goal.target} books\n                </span>\n              </p>\n            </div>\n            <span\n              className={`text-xs font-mono px-2 py-0.5 border ${\n                goal.state === \"completed\"\n                  ? \"border-green-300 dark:border-green-700 text-green-600 dark:text-green-400 bg-green-50 dark:bg-green-900/20\"\n                  : \"border-gray-200 dark:border-gray-700 text-gray-500 dark:text-gray-400\"\n              }`}\n            >\n              {goalPct}%\n            </span>\n          </div>\n\n          {/* Progress bar */}\n          <div className=\"h-1.5 bg-gray-100 dark:bg-neutral-700 overflow-hidden\">\n            <motion.div\n              initial={{ width: 0 }}\n              whileInView={{ width: `${goalPct}%` }}\n              viewport={{ once: true }}\n              transition={{ duration: 0.8, ease: \"easeOut\", delay: 0.2 }}\n              className={`h-full ${\n                goal.state === \"completed\"\n                  ? \"bg-green-500\"\n                  : \"bg-gray-900 dark:bg-white\"\n              }`}\n            />\n          </div>\n        </motion.div>\n      )}\n    </div>\n  );\n}\n\nconst STATUS_LABEL: Record<number, string> = {\n  1: \"Want to Read\",\n  2: \"Reading\",\n  3: \"Read\",\n};\n\n/* ── Page ── */\nexport default function BooksPage({\n  books,\n  profile,\n  error,\n}: {\n  books: HardcoverBook[];\n  profile: HardcoverProfile;\n  error: boolean;\n}) {\n  const [activeTab, setActiveTab] = useState<TabId>(3);\n  const [query, setQuery] = useState(\"\");\n  const [searchResults, setSearchResults] = useState<HardcoverBook[]>([]);\n\n  const handleSearch = debounce((value: string) => {\n    const trimmed = value.trim().toLowerCase();\n    setQuery(trimmed);\n    if (trimmed === \"\") {\n      setSearchResults([]);\n      return;\n    }\n    setSearchResults(\n      books.filter(\n        (b) =>\n          b.title.toLowerCase().includes(trimmed) ||\n          b.authors.some((a) => a.toLowerCase().includes(trimmed)),\n      ),\n    );\n  }, 300);\n\n  const isSearching = query.length > 0;\n\n  if (error) return <CreateAnIssue />;\n\n  const filtered = books.filter((b) => b.statusId === activeTab);\n  const countFor = (id: TabId) => books.filter((b) => b.statusId === id).length;\n\n  return (\n    <>\n      <MetaData\n        title={pageMeta.books.title}\n        description={pageMeta.books.description}\n        previewImage={pageMeta.books.image}\n        keywords={pageMeta.books.keywords}\n      />\n\n      <PageHeader\n        watermark=\"books\"\n        eyebrow=\"Books — 001\"\n        title=\"Books\"\n        description=\"Books I've read, am currently reading, or want to read. My personal bookshelf powered by Hardcover.\"\n        className=\"pb-24\"\n      >\n        {/* Stats */}\n        <BooksStats books={books} profile={profile} />\n\n        {/* Divider */}\n        <div className=\"flex items-center gap-3 mb-6\">\n          <div className=\"h-px w-5 bg-gray-400 dark:bg-neutral-600 flex-shrink-0\" />\n          <span className=\"font-mono text-[9px] tracking-[0.45em] uppercase text-gray-500 dark:text-gray-500\">\n            Bookshelf\n          </span>\n        </div>\n\n        {/* Search */}\n        <div className=\"relative mb-8\">\n          <CgSearch className=\"absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400 dark:text-neutral-600 pointer-events-none\" />\n          <input\n            type=\"text\"\n            onChange={(e) => handleSearch(e.target.value)}\n            placeholder=\"Search books by title or author…\"\n            className=\"w-full pl-9 pr-4 py-2.5 text-sm bg-white dark:bg-darkSecondary border border-gray-200 dark:border-neutral-700 text-gray-900 dark:text-white placeholder:text-gray-400 dark:placeholder:text-neutral-600 outline-none focus:border-gray-400 dark:focus:border-neutral-600 transition-colors font-mono\"\n          />\n        </div>\n\n        {/* Tab bar — hidden while searching */}\n        {!isSearching && (\n          <div className=\"flex items-center gap-1 mb-8 border-b border-gray-200 dark:border-neutral-700\">\n            {TABS.map((tab) => {\n              const isActive = activeTab === tab.id;\n              const count = countFor(tab.id);\n              return (\n                <button\n                  key={tab.id}\n                  onClick={() => setActiveTab(tab.id)}\n                  className={`relative px-4 py-2.5 text-sm font-medium transition-colors ${\n                    isActive\n                      ? \"text-gray-900 dark:text-white\"\n                      : \"text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200\"\n                  }`}\n                >\n                  {tab.label}\n                  <span\n                    className={`ml-1.5 text-xs font-mono ${\n                      isActive\n                        ? \"text-gray-500 dark:text-gray-400\"\n                        : \"text-gray-400 dark:text-neutral-600\"\n                    }`}\n                  >\n                    {count}\n                  </span>\n                  {isActive && (\n                    <motion.span\n                      layoutId=\"books-tab-underline\"\n                      className=\"absolute bottom-0 left-0 right-0 h-px bg-gray-900 dark:bg-white\"\n                    />\n                  )}\n                </button>\n              );\n            })}\n          </div>\n        )}\n\n        {/* Books grid — search results or tabbed view */}\n        {isSearching ? (\n          <>\n            <p className=\"text-xs font-mono text-gray-500 dark:text-gray-400 mb-6\">\n              {searchResults.length} result\n              {searchResults.length !== 1 ? \"s\" : \"\"} for &ldquo;{query}&rdquo;\n            </p>\n            {searchResults.length === 0 ? (\n              <p className=\"text-sm text-gray-500 dark:text-gray-400 py-8 text-center\">\n                No books found.\n              </p>\n            ) : (\n              <motion.div\n                variants={containerVariants}\n                initial=\"hidden\"\n                animate=\"visible\"\n                className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\"\n              >\n                {searchResults.map((book) => (\n                  <motion.div key={book.id} variants={fadeSlide}>\n                    <span className=\"text-[10px] font-mono tracking-[0.1em] uppercase text-gray-500 dark:text-gray-400 mb-1 block\">\n                      {STATUS_LABEL[book.statusId]}\n                    </span>\n                    <BookCard book={book} />\n                  </motion.div>\n                ))}\n              </motion.div>\n            )}\n          </>\n        ) : (\n          <AnimatePresence exitBeforeEnter>\n            <motion.div\n              key={activeTab}\n              variants={fadeSlide}\n              initial=\"hidden\"\n              animate=\"visible\"\n              exit=\"exit\"\n            >\n              {filtered.length === 0 ? (\n                <p className=\"text-sm text-gray-500 dark:text-gray-400 py-8 text-center\">\n                  Nothing here yet.\n                </p>\n              ) : (\n                <motion.div\n                  variants={containerVariants}\n                  initial=\"hidden\"\n                  animate=\"visible\"\n                  className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\"\n                >\n                  {filtered.map((book) => (\n                    <BookCard key={book.id} book={book} />\n                  ))}\n                </motion.div>\n              )}\n            </motion.div>\n          </AnimatePresence>\n        )}\n      </PageHeader>\n    </>\n  );\n}\n\nexport async function getStaticProps() {\n  try {\n    const [books, profile] = await Promise.all([getMyBooks(), getMyProfile()]);\n\n    return {\n      props: { books, profile, error: false },\n      revalidate: TIME_IN_SECONDS.ONE_DAY,\n    };\n  } catch {\n    return {\n      props: {\n        books: [],\n        profile: {\n          username: \"\",\n          name: \"\",\n          booksCount: 0,\n          currentYearGoal: null,\n        },\n        error: true,\n      },\n      revalidate: TIME_IN_SECONDS.ONE_DAY,\n    };\n  }\n}\n"
  },
  {
    "path": "pages/certificates.tsx",
    "content": "import MetaData from \"@components/MetaData\";\nimport Image from \"next/image\";\nimport Link from \"next/link\";\nimport PageHeader from \"@components/PageHeader\";\nimport pageMeta from \"@content/meta\";\nimport { CertificateType } from \"@lib/types\";\nimport { getCertificates } from \"@lib/supabase\";\nimport CreateAnIssue from \"@components/CreateAnIssue\";\nimport { getFormattedDate } from \"@utils/date\";\nimport { motion } from \"framer-motion\";\nimport { TIME_IN_SECONDS } from \"@utils/utils\";\n\nconst containerVariants = {\n  hidden: {},\n  visible: { transition: { staggerChildren: 0.04 } },\n};\n\nconst itemVariants = {\n  hidden: { opacity: 0, y: 12 },\n  visible: {\n    opacity: 1,\n    y: 0,\n    transition: { type: \"spring\", stiffness: 260, damping: 24 },\n  },\n};\n\nexport default function Certificates({\n  certificates,\n  error,\n}: {\n  certificates: CertificateType[];\n  error: boolean;\n}) {\n  if (error) return <CreateAnIssue />;\n\n  return (\n    <>\n      <MetaData\n        title={pageMeta.certificates.title}\n        description={pageMeta.certificates.description}\n        previewImage={pageMeta.certificates.image}\n        keywords={pageMeta.certificates.keywords}\n      />\n\n      <PageHeader\n        watermark=\"certs\"\n        eyebrow=\"Credentials — 001\"\n        title=\"Certificates\"\n        description=\"I've participated in many contests, courses and tests and earned certifications across a range of skills.\"\n        className=\"pb-24\"\n      >\n        <motion.div\n          variants={containerVariants}\n          initial=\"hidden\"\n          whileInView=\"visible\"\n          viewport={{ once: true, margin: \"-40px\" }}\n          className=\"flex flex-col border border-gray-200 dark:border-neutral-700\"\n        >\n          {certificates.map((cer) => (\n            <motion.div\n              key={cer.id}\n              variants={itemVariants}\n              className=\"flex items-center gap-4 p-4 border-b border-gray-200 dark:border-neutral-700 last:border-b-0 bg-white dark:bg-darkPrimary hover:bg-gray-50 dark:hover:bg-darkSecondary transition-colors\"\n            >\n              {/* Org logo */}\n              <div className=\"flex-shrink-0 w-10 h-10 border border-gray-200 dark:border-gray-700 flex items-center justify-center bg-white dark:bg-darkSecondary\">\n                <Image\n                  width={28}\n                  height={28}\n                  src={cer.orgLogo}\n                  alt={cer.orgName}\n                  quality={60}\n                  placeholder=\"blur\"\n                  blurDataURL={cer.orgLogo}\n                  style={{ objectFit: \"contain\" }}\n                />\n              </div>\n\n              {/* Title + meta */}\n              <div className=\"flex-1 min-w-0\">\n                <Link\n                  href={cer.url}\n                  target=\"_blank\"\n                  rel=\"noopener noreferrer\"\n                  className=\"text-sm font-semibold text-gray-900 dark:text-white hover:underline leading-snug line-clamp-1\"\n                >\n                  {cer.title}\n                </Link>\n                <p className=\"mt-0.5 font-mono text-[10px] tracking-[0.35em] uppercase text-gray-500 dark:text-gray-500\">\n                  {cer.orgName}\n                </p>\n              </div>\n\n              {/* Issued date */}\n              <span className=\"flex-shrink-0 font-mono text-[10px] tracking-[0.3em] uppercase text-gray-400 dark:text-gray-600 hidden sm:block\">\n                {getFormattedDate(new Date(cer.issuedDate))}\n              </span>\n            </motion.div>\n          ))}\n        </motion.div>\n      </PageHeader>\n    </>\n  );\n}\n\nexport async function getStaticProps() {\n  const { certificates, error } = await getCertificates();\n  return {\n    props: {\n      certificates,\n      error,\n    },\n    revalidate: TIME_IN_SECONDS.ONE_DAY, // Revalidate every 24 hours\n  };\n}\n"
  },
  {
    "path": "pages/epigraphs.tsx",
    "content": "import { useState, useMemo } from \"react\";\nimport { useDebounce } from \"@hooks/useDebounce\";\nimport { IEpigraph, EpigraphSourceType } from \"@lib/interface/sanity\";\nimport Metadata from \"@components/MetaData\";\nimport PageHeader from \"@components/PageHeader\";\nimport EpigraphCard from \"@components/EpigraphCard\";\nimport { getAllEpigraphs } from \"@lib/sanityContent\";\nimport pageMeta from \"@content/meta\";\nimport { motion } from \"framer-motion\";\nimport { TIME_IN_SECONDS } from \"@utils/utils\";\n\nconst SOURCE_TYPE_LABELS: Record<EpigraphSourceType | \"all\", string> = {\n  all: \"All\",\n  book: \"Books\",\n  movie: \"Movies\",\n  tvShow: \"TV Shows\",\n  person: \"People\",\n  song: \"Songs\",\n  podcast: \"Podcasts\",\n  other: \"Other\",\n};\n\nexport default function Epigraphs({ epigraphs }: { epigraphs: IEpigraph[] }) {\n  const [activeType, setActiveType] = useState<EpigraphSourceType | \"all\">(\n    \"all\",\n  );\n  const [search, setSearch] = useState(\"\");\n  const debouncedSearch = useDebounce(search, 300);\n\n  // Derive which source types actually exist in the data\n  const availableTypes = useMemo<Array<EpigraphSourceType | \"all\">>(() => {\n    const types = new Set(epigraphs.map((e) => e.sourceType));\n    const ordered: Array<EpigraphSourceType | \"all\"> = [\n      \"all\",\n      \"book\",\n      \"movie\",\n      \"tvShow\",\n      \"person\",\n      \"song\",\n      \"podcast\",\n      \"other\",\n    ];\n    return ordered.filter(\n      (t) => t === \"all\" || types.has(t as EpigraphSourceType),\n    );\n  }, [epigraphs]);\n\n  const filtered = useMemo(() => {\n    const q = debouncedSearch.trim().toLowerCase();\n    return epigraphs.filter((m) => {\n      const typeMatch = activeType === \"all\" || m.sourceType === activeType;\n      if (!typeMatch) return false;\n      if (!q) return true;\n      return (\n        m.quote.toLowerCase().includes(q) ||\n        m.sourceTitle.toLowerCase().includes(q) ||\n        (m.sourceMeta?.toLowerCase().includes(q) ?? false) ||\n        (m.speaker?.toLowerCase().includes(q) ?? false) ||\n        (m.tags?.some((t) => t.toLowerCase().includes(q)) ?? false)\n      );\n    });\n  }, [epigraphs, activeType, debouncedSearch]);\n\n  return (\n    <>\n      <Metadata\n        title={pageMeta.epigraphs.title}\n        description={pageMeta.epigraphs.description}\n        previewImage={pageMeta.epigraphs.image}\n        keywords={pageMeta.epigraphs.keywords}\n      />\n\n      <PageHeader\n        watermark=\"epigraphs\"\n        eyebrow=\"Epigraphs — 001\"\n        title=\"Epigraphs\"\n        description={`Quotes, passages, and stanzas that caught my eye. ${epigraphs.length} collected so far.`}\n        className=\"pb-8\"\n      >\n        {/* ── Filter Controls ── */}\n        <div className=\"flex flex-col sm:flex-row gap-4 mb-10\">\n          {/* Search */}\n          <div className=\"relative flex-1 max-w-sm\">\n            <input\n              type=\"search\"\n              placeholder=\"Search epigraphs…\"\n              value={search}\n              onChange={(e) => setSearch(e.target.value)}\n              className=\"w-full bg-white dark:bg-darkSecondary border border-gray-200 dark:border-neutral-700 text-sm text-gray-900 dark:text-gray-100 placeholder-gray-400 dark:placeholder-gray-600 px-4 py-2 focus:outline-none focus:border-gray-400 dark:focus:border-gray-500 transition-colors\"\n            />\n          </div>\n\n          {/* Source type filter pills */}\n          <div className=\"flex flex-wrap gap-2\">\n            {availableTypes.map((type) => (\n              <button\n                key={type}\n                onClick={() => setActiveType(type)}\n                className={`font-mono text-[10px] tracking-[0.3em] uppercase px-3 py-1.5 border transition-colors ${\n                  activeType === type\n                    ? \"bg-gray-900 dark:bg-white text-white dark:text-gray-900 border-gray-900 dark:border-white\"\n                    : \"bg-transparent text-gray-500 dark:text-gray-500 border-gray-200 dark:border-neutral-700 hover:border-gray-400 dark:hover:border-gray-500\"\n                }`}\n              >\n                {SOURCE_TYPE_LABELS[type]}\n              </button>\n            ))}\n          </div>\n        </div>\n\n        {/* Result count */}\n        {(debouncedSearch || activeType !== \"all\") && (\n          <motion.p\n            initial={{ opacity: 0 }}\n            animate={{ opacity: 1 }}\n            className=\"font-mono text-[10px] tracking-widest uppercase text-gray-400 dark:text-gray-600 mb-6\"\n          >\n            {filtered.length} result{filtered.length !== 1 ? \"s\" : \"\"}\n          </motion.p>\n        )}\n\n        {/* ── List ── */}\n        {filtered.length > 0 ? (\n          <motion.div\n            key={`${activeType}-${debouncedSearch}`}\n            initial=\"hidden\"\n            animate=\"visible\"\n            variants={{\n              hidden: {},\n              visible: { transition: { staggerChildren: 0.06 } },\n            }}\n            className=\"divide-y divide-gray-400/20 dark:divide-neutral-700 pb-16\"\n          >\n            {filtered.map((epigraph, i) => (\n              <EpigraphCard key={epigraph._id} epigraph={epigraph} index={i} />\n            ))}\n          </motion.div>\n        ) : (\n          <div className=\"flex flex-col items-center justify-center py-24 text-center pb-16\">\n            <span className=\"font-serif text-7xl text-gray-200 dark:text-neutral-800 select-none leading-none\">\n              &ldquo;\n            </span>\n            <p className=\"mt-6 text-gray-500 dark:text-gray-500 text-sm\">\n              No epigraphs match your filters.\n            </p>\n            <button\n              onClick={() => {\n                setSearch(\"\");\n                setActiveType(\"all\");\n              }}\n              className=\"mt-4 font-mono text-[10px] tracking-widest uppercase text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 transition-colors\"\n            >\n              Clear filters\n            </button>\n          </div>\n        )}\n      </PageHeader>\n    </>\n  );\n}\n\nexport async function getStaticProps() {\n  const epigraphs = await getAllEpigraphs();\n\n  return {\n    props: { epigraphs },\n    revalidate: TIME_IN_SECONDS.ONE_DAY, // revalidate every 5 minutes\n  };\n}\n"
  },
  {
    "path": "pages/index.tsx",
    "content": "// Page Components START----------\n\nimport { headingFromLeft } from \"@content/FramerMotionVariants\";\n\nimport AnimatedHeading from \"@components/FramerMotion/AnimatedHeading\";\nimport { BlogPost } from \"@lib/interface/sanity\";\nimport { IEpigraph } from \"@lib/interface/sanity\";\nimport BlogsSection from \"@components/Home/BlogsSection\";\nimport EpigraphsSection from \"@components/Home/EpigraphsSection\";\nimport Contact from \"@components/Contact\";\nimport HeroSection from \"@components/Home/HeroSection\";\nimport Metadata from \"@components/MetaData\";\nimport React from \"react\";\nimport SkillSection from \"@components/Home/SkillSection\";\nimport generateSitemap from \"@lib/sitemap\";\nimport {\n  getAllPostsMeta,\n  getPostCount,\n  getAllEpigraphs,\n  getEpigraphCount,\n} from \"@lib/sanityContent\";\nimport getRSS from \"@lib/generateRSS\";\nimport pageMeta from \"@content/meta\";\nimport { TIME_IN_SECONDS } from \"@utils/utils\";\n\nexport default function Home({\n  blogs,\n  totalBlogs,\n  epigraphs,\n  totalEpigraphs,\n}: {\n  blogs: BlogPost[];\n  totalBlogs: number;\n  epigraphs: IEpigraph[];\n  totalEpigraphs: number;\n}) {\n  return (\n    <>\n      <Metadata\n        title=\"Jatin Sharma\"\n        description={pageMeta.home.description}\n        previewImage={pageMeta.home.image}\n        keywords={pageMeta.home.keywords}\n      />\n      <div className=\"relative w-full dark:bg-darkPrimary dark:text-gray-100\">\n        <HeroSection />\n\n        {/* Content Sections */}\n        <div className=\"w-full\">\n          <div className=\"max-w-7xl mx-auto px-6 sm:px-8 lg:px-12\">\n            <SkillSection />\n            <BlogsSection blogs={blogs} totalBlogs={totalBlogs} />\n            <EpigraphsSection\n              epigraphs={epigraphs}\n              totalEpigraphs={totalEpigraphs}\n            />\n            <Contact />\n          </div>\n        </div>\n      </div>\n    </>\n  );\n}\n\nexport function HomeHeading({ title }: { title: React.ReactNode | string }) {\n  return (\n    <AnimatedHeading\n      className=\"w-full my-2 text-2xl sm:text-3xl font-bold text-left font-inter\"\n      variants={headingFromLeft}\n    >\n      {title}\n    </AnimatedHeading>\n  );\n}\n\nexport async function getStaticProps() {\n  const [blogs, totalBlogs, epigraphs, totalEpigraphs] = await Promise.all([\n    getAllPostsMeta(3),\n    getPostCount(),\n    getAllEpigraphs(5),\n    getEpigraphCount(),\n  ]);\n\n  // RSS and sitemap are generated at build time only.\n  // They are not regenerated on ISR revalidations because they write\n  // to the filesystem which is read-only on most hosting platforms after build.\n  if (\n    process.env.NODE_ENV === \"production\" &&\n    process.env.NEXT_PHASE === \"phase-production-build\"\n  ) {\n    await getRSS();\n    await generateSitemap();\n  }\n\n  return {\n    props: { blogs, totalBlogs, epigraphs, totalEpigraphs },\n    revalidate: TIME_IN_SECONDS.TEN_MINUTES, // revalidate every 10 minutes\n  };\n}\n"
  },
  {
    "path": "pages/privacy.tsx",
    "content": "import { IStaticPage } from \"@lib/interface/sanity\";\nimport StaticPage from \"@components/StaticPage\";\nimport { getStaticPageFromSlug } from \"@lib/sanityContent\";\nimport pageMeta from \"@content/meta\";\nimport { TIME_IN_SECONDS } from \"@utils/utils\";\n\nexport default function Privacy({\n  privacyPolicy,\n}: {\n  privacyPolicy: IStaticPage;\n}) {\n  return <StaticPage metadata={pageMeta.privacy} page={privacyPolicy} />;\n}\n\nexport async function getStaticProps() {\n  const privacyPolicy = await getStaticPageFromSlug(\"privacy\");\n\n  return {\n    props: {\n      privacyPolicy,\n    },\n    revalidate: TIME_IN_SECONDS.ONE_WEEK,\n  };\n}\n"
  },
  {
    "path": "pages/projects.tsx",
    "content": "import React from \"react\";\nimport Project from \"@components/Project\";\nimport Metadata from \"@components/MetaData\";\nimport PageHeader from \"@components/PageHeader\";\nimport pageMeta from \"@content/meta\";\nimport { getProjects } from \"@lib/supabase\";\nimport { ProjectType } from \"@lib/types\";\nimport CreateAnIssue from \"@components/CreateAnIssue\";\nimport { motion } from \"framer-motion\";\nimport { TIME_IN_SECONDS } from \"@utils/utils\";\n\nconst containerVariants = {\n  hidden: {},\n  visible: { transition: { staggerChildren: 0.06 } },\n};\n\nexport default function Projects({\n  projects,\n  error,\n}: {\n  projects: ProjectType[];\n  error: boolean;\n}) {\n  if (error) return <CreateAnIssue />;\n\n  const visible = projects.filter(\n    (p) => !(p.name === \"\" && p.githubURL === \"\"),\n  );\n\n  return (\n    <>\n      <Metadata\n        title={pageMeta.projects.title}\n        description={pageMeta.projects.description}\n        previewImage={pageMeta.projects.image}\n        keywords={pageMeta.projects.keywords}\n      />\n\n      <PageHeader\n        watermark=\"work\"\n        eyebrow=\"Projects — 001\"\n        title=\"Projects\"\n        description={`I've built various projects ranging from simple experiments to complex applications. ${visible.length}+ projects and counting.`}\n        className=\"pb-24\"\n      >\n        <motion.div\n          variants={containerVariants}\n          initial=\"hidden\"\n          whileInView=\"visible\"\n          viewport={{ once: true, margin: \"-40px\" }}\n          className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\"\n        >\n          {visible.map((project, index) => (\n            <Project\n              key={project.id}\n              project={project}\n              featured={index === 0}\n            />\n          ))}\n        </motion.div>\n      </PageHeader>\n    </>\n  );\n}\n\nexport async function getStaticProps() {\n  const { projects, error } = await getProjects();\n  return {\n    props: {\n      projects,\n      error,\n    },\n    revalidate: TIME_IN_SECONDS.ONE_DAY,\n  };\n}\n"
  },
  {
    "path": "pages/snippets/[slug].tsx",
    "content": "import { getAllSlugs, getSnippetFromSlug } from \"@lib/sanityContent\";\n\nimport { GetStaticPropsContext } from \"next\";\nimport { ISnippet } from \"@lib/interface/sanity\";\nimport MDXComponents from \"@components/MDXComponents\";\nimport { MDXRemote } from \"next-mdx-remote\";\nimport Metadata from \"@components/MetaData\";\nimport PageNotFound from \"@components/PageNotFound\";\nimport SnippetLayout from \"@layout/SnippetLayout\";\nimport pageMeta from \"@content/meta\";\nimport { TIME_IN_SECONDS } from \"@utils/utils\";\n\nexport default function SnippetPage({\n  snippet,\n  error,\n}: {\n  snippet: ISnippet;\n  error: boolean;\n}) {\n  if (error) return <PageNotFound />;\n\n  return (\n    <>\n      <Metadata\n        title={snippet.title}\n        suffix=\"Jatin Sharma\"\n        description={snippet.excerpt}\n        previewImage={pageMeta.snippets.image}\n        keywords={pageMeta.snippets.keywords}\n      />\n\n      <SnippetLayout snippet={snippet}>\n        <MDXRemote\n          {...snippet.content}\n          frontmatter={{\n            slug: snippet.slug.current,\n            excerpt: snippet.excerpt,\n            title: snippet.title,\n            date: snippet.publishedAt,\n            image: pageMeta.snippets.image,\n          }}\n          components={MDXComponents}\n        />\n      </SnippetLayout>\n    </>\n  );\n}\n\ntype StaticProps = GetStaticPropsContext & {\n  params: {\n    slug: string;\n  };\n};\n\nexport async function getStaticProps({ params }: StaticProps) {\n  const { slug } = params;\n\n  const snippet = await getSnippetFromSlug(slug);\n\n  if (snippet != null) {\n    return {\n      props: {\n        error: false,\n        snippet,\n      },\n      revalidate: TIME_IN_SECONDS.ONE_DAY,\n    };\n  } else {\n    return {\n      props: {\n        error: true,\n        snippet: null,\n      },\n      revalidate: TIME_IN_SECONDS.ONE_DAY,\n    };\n  }\n}\n\nexport async function getStaticPaths() {\n  const slugs = await getAllSlugs({\n    type: \"snippet\",\n  });\n  const paths = slugs.map((slug: any) => ({ params: { slug } }));\n\n  return {\n    paths,\n    fallback: \"blocking\",\n  };\n}\n"
  },
  {
    "path": "pages/snippets/index.tsx",
    "content": "import { motion } from \"framer-motion\";\nimport { ISnippet } from \"@lib/interface/sanity\";\nimport Metadata from \"@components/MetaData\";\nimport PageHeader from \"@components/PageHeader\";\nimport SnippetCard from \"@components/SnippetCard\";\nimport { getAllSnippetsMeta } from \"@lib/sanityContent\";\nimport pageMeta from \"@content/meta\";\nimport { TIME_IN_SECONDS } from \"@utils/utils\";\n\nconst containerVariants = {\n  hidden: {},\n  visible: { transition: { staggerChildren: 0.05 } },\n};\n\nexport default function Snippets({ snippets }: { snippets: ISnippet[] }) {\n  return (\n    <>\n      <Metadata\n        title={pageMeta.snippets.title}\n        description={pageMeta.snippets.description}\n        previewImage={pageMeta.snippets.image}\n        keywords={pageMeta.snippets.keywords}\n      />\n\n      <PageHeader\n        watermark=\"snippets\"\n        eyebrow=\"Snippets — 001\"\n        title=\"Code Snippets\"\n        description={`A collection of reusable code snippets I've saved over time. ${snippets.length} snippets available.`}\n        className=\"pb-24\"\n      >\n        <motion.div\n          variants={containerVariants}\n          initial=\"hidden\"\n          whileInView=\"visible\"\n          viewport={{ once: true, margin: \"-40px\" }}\n          className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-px bg-gray-200 dark:bg-neutral-700 border border-gray-200 dark:border-neutral-700\"\n        >\n          {snippets.map((snippet, index) => (\n            <SnippetCard key={index} snippet={snippet} />\n          ))}\n        </motion.div>\n      </PageHeader>\n    </>\n  );\n}\n\nexport async function getStaticProps() {\n  const snippets = await getAllSnippetsMeta();\n\n  return {\n    props: { snippets },\n    revalidate: TIME_IN_SECONDS.ONE_DAY, // Revalidate every 24 hours\n  };\n}\n"
  },
  {
    "path": "pages/stats.tsx",
    "content": "// import { SpotifyArtist, SpotifyTrack } from \"@lib/types\";\n\n// import Artist from \"@components/Stats/Artist\";\nimport GitHubActivityGraph from \"@components/GitHubActivityGraph\";\nimport GitHubCalendar from \"react-github-calendar\";\nimport MetaData from \"@components/MetaData\";\nimport MonkeyTypeStats from \"@components/Stats/MonkeyTypeStats\";\nimport PageHeader from \"@components/PageHeader\";\nimport React from \"react\";\nimport StatsCard from \"@components/Stats/StatsCard\";\nconsole.log(\"Stats page rendered\");\n// import Track from \"@components/Stats/Track\";\nimport fetcher from \"@lib/fetcher\";\nimport pageMeta from \"@content/meta\";\nimport { motion } from \"framer-motion\";\nimport { useDarkMode } from \"@context/darkModeContext\";\nimport useSWR from \"swr\";\n\ntype Stats = {\n  title: string;\n  value: string;\n};\n\nconst containerVariants = {\n  hidden: {},\n  visible: { transition: { staggerChildren: 0.06 } },\n};\n\n/* Shared section-header used by each sub-section */\nfunction SectionHeading({\n  eyebrow,\n  title,\n  description,\n}: {\n  eyebrow: string;\n  title: string;\n  description?: React.ReactNode;\n}) {\n  return (\n    <div className=\"mb-8 space-y-3\">\n      <motion.div\n        initial={{ opacity: 0, x: -16 }}\n        whileInView={{ opacity: 1, x: 0 }}\n        viewport={{ once: true }}\n        className=\"flex items-center gap-3\"\n      >\n        <div className=\"h-px w-5 bg-gray-400 dark:bg-gray-600 flex-shrink-0\" />\n        <span className=\"font-mono text-[10px] tracking-[0.45em] uppercase text-gray-500 dark:text-gray-500\">\n          {eyebrow}\n        </span>\n      </motion.div>\n      <motion.h2\n        initial={{ opacity: 0, y: 16 }}\n        whileInView={{ opacity: 1, y: 0 }}\n        viewport={{ once: true }}\n        transition={{ delay: 0.08 }}\n        className=\"text-2xl sm:text-3xl font-bold text-gray-900 dark:text-white\"\n      >\n        {title}\n      </motion.h2>\n      {description && (\n        <motion.p\n          initial={{ opacity: 0, y: 12 }}\n          whileInView={{ opacity: 1, y: 0 }}\n          viewport={{ once: true }}\n          transition={{ delay: 0.14 }}\n          className=\"text-sm text-gray-600 dark:text-gray-400 border-l-2 border-gray-300 dark:border-gray-700 pl-4 py-0.5 max-w-2xl\"\n        >\n          {description}\n        </motion.p>\n      )}\n    </div>\n  );\n}\n\nexport default function Stats() {\n  const { isDarkMode } = useDarkMode();\n\n  // const { data: topTracks } = useSWR(\"/api/stats/tracks\", fetcher);\n  // const { data: artists } = useSWR(\"/api/stats/artists\", fetcher);\n  const { data: devto } = useSWR(\"/api/stats/devto\", fetcher);\n  const { data: github } = useSWR(\"/api/stats/github\", fetcher);\n\n  const devtoStats: Stats[] = [\n    { title: \"Total Posts\", value: devto?.posts.toLocaleString() },\n    { title: \"Followers\", value: devto?.followers.toLocaleString() },\n    { title: \"Reactions\", value: devto?.likes.toLocaleString() },\n    { title: \"Views\", value: devto?.views.toLocaleString() },\n    { title: \"Comments\", value: devto?.comments.toLocaleString() },\n  ];\n\n  const githubStats: Stats[] = [\n    { title: \"Repos\", value: github?.repos },\n    { title: \"Gists\", value: github?.gists },\n    { title: \"Followers\", value: github?.followers },\n    { title: \"Stars\", value: github?.githubStars },\n    { title: \"Forked\", value: github?.forks },\n  ];\n\n  return (\n    <>\n      <MetaData\n        title={pageMeta.stats.title}\n        description={pageMeta.stats.description}\n        previewImage={pageMeta.stats.image}\n        keywords={pageMeta.stats.keywords}\n      />\n\n      <PageHeader\n        watermark=\"stats\"\n        eyebrow=\"Dashboard — 001\"\n        title=\"Statistics\"\n        description=\"Personal stats about my Dev.to blogs, GitHub activity, and typing performance.\"\n        className=\"pb-24\"\n      >\n        {/* ═══════════════════════════════════════════ */}\n        {/* Dev.to Section                              */}\n        {/* ═══════════════════════════════════════════ */}\n        <div className=\"mb-20\">\n          <SectionHeading\n            eyebrow=\"Dev.to\"\n            title=\"Blog Statistics\"\n            description=\"Writing metrics from my Dev.to blog including posts, followers, reactions, views, and comments.\"\n          />\n          <motion.div\n            variants={containerVariants}\n            initial=\"hidden\"\n            whileInView=\"visible\"\n            viewport={{ once: true, margin: \"-60px\" }}\n            className=\"grid grid-cols-2 sm:grid-cols-3 xl:grid-cols-5 gap-px bg-gray-200 dark:bg-darkSecondary border border-gray-200 dark:border-darkSecondary\"\n          >\n            {devtoStats.map((stat, index) => (\n              <StatsCard key={index} title={stat.title} value={stat.value} />\n            ))}\n          </motion.div>\n        </div>\n\n        {/* ═══════════════════════════════════════════ */}\n        {/* GitHub Section                              */}\n        {/* ═══════════════════════════════════════════ */}\n        <div className=\"mb-20\">\n          <SectionHeading\n            eyebrow=\"GitHub\"\n            title=\"GitHub Activity\"\n            description=\"Open source contributions, repositories, and coding activity on GitHub.\"\n          />\n          <motion.div\n            variants={containerVariants}\n            initial=\"hidden\"\n            whileInView=\"visible\"\n            viewport={{ once: true, margin: \"-60px\" }}\n            className=\"grid grid-cols-2 sm:grid-cols-3 xl:grid-cols-5 gap-px bg-gray-200 dark:bg-darkSecondary border border-gray-200 dark:border-darkSecondary mb-12\"\n          >\n            {githubStats.map((stat, index) => (\n              <StatsCard key={index} title={stat.title} value={stat.value} />\n            ))}\n          </motion.div>\n\n          {/* Contribution calendar */}\n          <div className=\"mb-12\">\n            <div className=\"overflow-x-auto text-black dark:text-white\">\n              <GitHubCalendar\n                username=\"j471n\"\n                colorScheme={isDarkMode ? \"dark\" : \"light\"}\n              />\n            </div>\n          </div>\n\n          {/* GitHub charts — 2 col grid */}\n          <GitHubActivityGraph />\n        </div>\n\n        {/* ═══════════════════════════════════════════ */}\n        {/* MonkeyType Section                          */}\n        {/* ═══════════════════════════════════════════ */}\n        <MonkeyTypeStats />\n\n        {/* ── Top Tracks ── */}\n        {/* <div className=\"mb-16\">\n          <SectionHeading\n            eyebrow=\"Spotify\"\n            title=\"Top Streamed Songs\"\n            description={\n              <>\n                {topTracks ? (\n                  <>\n                    <span className=\"font-semibold text-gray-900 dark:text-white\">\n                      {topTracks?.[0]?.title}\n                    </span>\n                    {\" is my most streamed track in the last 4 weeks.\"}\n                  </>\n                ) : (\n                  \"My top tracks on Spotify, updated daily.\"\n                )}\n              </>\n            }\n          />\n          <div className=\"flex flex-col border border-gray-200 dark:border-neutral-700\">\n            {topTracks ? (\n              topTracks.map((track: SpotifyTrack, index: number) => (\n                <Track\n                  key={index}\n                  id={index}\n                  url={track.url}\n                  title={track.title}\n                  coverImage={track.coverImage.url}\n                  artist={track.artist}\n                />\n              ))\n            ) : (\n              <LoadingSongs />\n            )}\n          </div>\n        </div> */}\n\n        {/* ── Top Artists ── */}\n        {/* <div className=\"mb-4\">\n          <SectionHeading\n            eyebrow=\"Spotify\"\n            title=\"Top Artists\"\n            description={\n              <>\n                {artists ? (\n                  <>\n                    {\"My most listened artist is \"}\n                    <span className=\"font-semibold text-gray-900 dark:text-white\">\n                      {artists?.[0]?.name}\n                    </span>\n                    {\" in the last 4 weeks.\"}\n                  </>\n                ) : (\n                  \"My most listened artists on Spotify in the last 4 weeks.\"\n                )}\n              </>\n            }\n          />\n          <div className=\"flex flex-col border border-gray-200 dark:border-neutral-700\">\n            {artists ? (\n              artists.length === 0 ? (\n                <p className=\"p-6 text-sm font-mono text-gray-500 dark:text-gray-500\">\n                  Not enough data to show.\n                </p>\n              ) : (\n                artists.map((artist: SpotifyArtist, index: number) => (\n                  <Artist\n                    key={index}\n                    id={index}\n                    name={artist.name!}\n                    url={artist.url}\n                    coverImage={artist.coverImage.url}\n                    popularity={artist.popularity!}\n                  />\n                ))\n              )\n            ) : (\n              <LoadingArtists />\n            )}\n          </div>\n        </div> */}\n      </PageHeader>\n    </>\n  );\n}\n\n// function LoadingSongs() {\n//   return (\n//     <>\n//       {Array.from({ length: 10 }, (_, i) => (\n//         <div\n//           key={i}\n//           className=\"flex items-center gap-4 p-4 border-b border-gray-200 dark:border-neutral-700 bg-white dark:bg-darkPrimary\"\n//         >\n//           <span className=\"font-mono text-[10px] text-gray-400 dark:text-gray-600 w-6 text-right flex-shrink-0\">\n//             {i + 1}\n//           </span>\n//           <div className=\"w-10 h-10 bg-gray-200 dark:bg-neutral-700 animate-pulse flex-shrink-0\" />\n//           <div className=\"flex flex-col gap-1.5 flex-1\">\n//             <div className=\"h-3.5 w-40 bg-gray-200 dark:bg-neutral-700 animate-pulse\" />\n//             <div className=\"h-2.5 w-24 bg-gray-200 dark:bg-neutral-700 animate-pulse\" />\n//           </div>\n//         </div>\n//       ))}\n//     </>\n//   );\n// }\n\n// function LoadingArtists() {\n//   return (\n//     <>\n//       {Array.from({ length: 5 }, (_, i) => (\n//         <div\n//           key={i}\n//           className=\"flex items-center gap-4 p-4 border-b border-gray-200 dark:border-neutral-700 bg-white dark:bg-darkPrimary\"\n//         >\n//           <span className=\"font-mono text-[10px] text-gray-400 dark:text-gray-600 w-6 text-right flex-shrink-0\">\n//             {i + 1}\n//           </span>\n//           <div className=\"w-12 h-12 rounded-full bg-gray-200 dark:bg-neutral-700 animate-pulse flex-shrink-0\" />\n//           <div className=\"flex flex-col gap-1.5 flex-1\">\n//             <div className=\"h-3.5 w-40 bg-gray-200 dark:bg-neutral-700 animate-pulse\" />\n//             <div className=\"h-2.5 w-24 bg-gray-200 dark:bg-neutral-700 animate-pulse\" />\n//           </div>\n//         </div>\n//       ))}\n//     </>\n//   );\n// }\n"
  },
  {
    "path": "pages/tet.json",
    "content": "{\n  \"public_identifier\": \"j471n\",\n  \"profile_pic_url\": \"https://i.imgur.com/RF2bEls.jpg\",\n  \"background_cover_image_url\": null,\n  \"first_name\": \"Jatin\",\n  \"last_name\": \"Sharma\",\n  \"full_name\": \"Jatin Sharma\",\n  \"follower_count\": 843,\n  \"occupation\": \"Trusted Member at Forem\",\n  \"headline\": \"React Developer | Next.js | Blogger @DEV Community\",\n  \"summary\": \"A front-end developer who specializes in React.js and Next.js. I have experience building and maintaining web applications. I build web applications using React, Next.js and TailwindCSS. I am always looking for new challenges and ways to work more efficiently. I am also familiar with Python & C++.\\n\\nThe one things I like the most is blogging. I post blogs on dev.to and share my experience and skills with others. \\n\\nWhen I am not working, you can find me exploring the frictional world through movies and web series.\",\n  \"country\": \"IN\",\n  \"country_full_name\": \"India\",\n  \"city\": \"Bareilly\",\n  \"state\": \"Uttar Pradesh\",\n  \"experiences\": [\n    {\n      \"company\": \"Forem\",\n      \"company_linkedin_profile_url\": \"https://www.linkedin.com/company/thepracticaldev/\",\n      \"logo_url\": \"https://i.imgur.com/ONNBTJH.jpg\",\n      \"job_titles\": [\n        {\n          \"starts_at\": {\n            \"day\": 1,\n            \"month\": 10,\n            \"year\": 2021\n          },\n          \"ends_at\": null,\n          \"title\": \"Blogger @DEV Community\",\n          \"description\": \"I research and study a subject, and then I compose an article about it. I introduce new and different topics and provide an in-depth explanation that is accessible to those who have no prior technical understanding of the subject matter. I ensure that the information is presented in a clear and in very simple language, making it accessible to readers of all skill levels even for those who may not have a strong command of the English language.\\n\\n\\u2605 7.5K+ Followers\\n\\u2605 520K+ Post Views\\n\\u2605 Top 7 Author\\n\\u2605 100+ Posts\\n\\u2605 12K+ Reactions\",\n          \"location\": \"India\"\n        },\n        {\n          \"starts_at\": {\n            \"day\": 1,\n            \"month\": 5,\n            \"year\": 2023\n          },\n          \"location\": \"Remote\",\n          \"ends_at\": null,\n          \"title\": \"Trusted Member\",\n          \"description\": \"As a Trusted Member, I have been granted basic moderation abilities to help \\ud835\\uddd7\\ud835\\uddf2\\ud835\\ude03 \\ud835\\uddd6\\ud835\\uddfc\\ud835\\uddfa\\ud835\\uddfa\\ud835\\ude02\\ud835\\uddfb\\ud835\\uddf6\\ud835\\ude01\\ud835\\ude06 by moderate discussions and ensure positive interactions among community members. This includes assisting with reporting problematic content and potentially harmful behavior. Being designated as a Trusted Member is an honor and a great responsibility, and it reinforces the importance of maintaining a respectful and inclusive community.\"\n        },\n        {\n          \"starts_at\": {\n            \"day\": 1,\n            \"month\": 9,\n            \"year\": 2022\n          },\n          \"ends_at\": {\n            \"day\": 1,\n            \"month\": 9,\n            \"year\": 2022\n          },\n          \"title\": \"Featured in Top 7 authors of the week\",\n          \"description\": \"I have been featured in Top 7 authors of the week twice.\",\n          \"location\": null\n        }\n      ]\n    },\n    {\n      \"company\": \"KonnectNXT\",\n      \"company_linkedin_profile_url\": \"https://www.linkedin.com/company/konnectnxt/\",\n      \"logo_url\": \"https://i.imgur.com/TaEWZcc.jpg\",\n      \"job_titles\": [\n        {\n          \"title\": \"Software Developer\",\n          \"description\": null,\n          \"location\": \"Hyderabad, Telangana, India\",\n          \"starts_at\": {\n            \"day\": 1,\n            \"month\": 2,\n            \"year\": 2023\n          },\n          \"ends_at\": null\n        }\n      ]\n    },\n    {\n      \"company\": \"Documatic\",\n      \"company_linkedin_profile_url\": \"https://www.linkedin.com/company/trydocumatic/\",\n      \"logo_url\": \"https://i.imgur.com/4ogaHWS.jpg\",\n      \"job_titles\": [\n        {\n          \"starts_at\": {\n            \"day\": 1,\n            \"month\": 2,\n            \"year\": 2023\n          },\n          \"ends_at\": null,\n          \"title\": \"Technical Writer\",\n          \"description\": null,\n          \"location\": \"United Kingdom\"\n        }\n      ]\n    },\n    {\n      \"company\": \"Substack\",\n      \"company_linkedin_profile_url\": \"https://www.linkedin.com/company/substack/\",\n      \"logo_url\": \"https://i.imgur.com/HgVfweU.jpg\",\n      \"job_titles\": [\n        {\n          \"starts_at\": {\n            \"day\": 1,\n            \"month\": 5,\n            \"year\": 2022\n          },\n          \"ends_at\": null,\n          \"title\": \"Writer\",\n          \"description\": \"As a writer, my focus is primarily on the topics of web development and productivity chrome extensions. Through my writing, I aim to provide detailed and comprehensive coverage of the latest developments and trends in the field of web development, as well as share tips and tricks for using productivity chrome extensions to increase efficiency and productivity. I also ensure that they never miss any of my blog article. If they do then this is the reminder for them.\",\n          \"location\": \"India\"\n        }\n      ]\n    },\n    {\n      \"company\": \"Hashnode\",\n      \"company_linkedin_profile_url\": \"https://www.linkedin.com/company/hashnode/\",\n      \"logo_url\": \"https://i.imgur.com/vwegEfc.jpg\",\n      \"job_titles\": [\n        {\n          \"starts_at\": {\n            \"day\": 1,\n            \"month\": 11,\n            \"year\": 2021\n          },\n          \"ends_at\": null,\n          \"title\": \"Blogger\",\n          \"description\": \"I am keeping a record of my programming journey to monitor my growth and evaluate the difficulties and successes I encounter.\",\n          \"location\": \"India\"\n        }\n      ]\n    },\n    {\n      \"company\": \"Medium\",\n      \"company_linkedin_profile_url\": \"https://www.linkedin.com/company/medium-com/\",\n      \"logo_url\": \"https://i.imgur.com/hT7ajHV.jpg\",\n      \"job_titles\": [\n        {\n          \"starts_at\": {\n            \"day\": 1,\n            \"month\": 10,\n            \"year\": 2020\n          },\n          \"ends_at\": null,\n          \"title\": \"Blogger\",\n          \"description\": \"I am an author who focuses on web development and offers a wide variety of beginner-friendly tutorials. Through my writings, I aim to pass on my knowledge and experience to others who share my interest in all aspects of programming.\",\n          \"location\": \"India\"\n        }\n      ]\n    }\n  ],\n  \"education\": [\n    {\n      \"starts_at\": {\n        \"day\": 1,\n        \"month\": 1,\n        \"year\": 2019\n      },\n      \"ends_at\": {\n        \"day\": 1,\n        \"month\": 1,\n        \"year\": 2023\n      },\n      \"field_of_study\": \"Computer Science\",\n      \"degree_name\": \"Bachelor of Technology - B.Tech\",\n      \"school\": \"Dr. A.P.J. Abdul Kalam Technical University\",\n      \"school_linkedin_profile_url\": null,\n      \"description\": null,\n      \"logo_url\": \"https://i.imgur.com/faTfpD7.jpg\",\n      \"grade\": null,\n      \"activities_and_societies\": null\n    }\n  ],\n  \"languages\": [\n    \"English\",\n    \"Hindi\"\n  ],\n  \"accomplishment_organisations\": [],\n  \"accomplishment_publications\": [],\n  \"accomplishment_honors_awards\": [],\n  \"accomplishment_patents\": [],\n  \"accomplishment_courses\": [],\n  \"accomplishment_projects\": [],\n  \"accomplishment_test_scores\": [],\n  \"volunteer_work\": [\n    {\n      \"starts_at\": {\n        \"day\": 1,\n        \"month\": 1,\n        \"year\": 2023\n      },\n      \"ends_at\": null,\n      \"title\": \"Member\",\n      \"cause\": \"EDUCATION\",\n      \"company\": \"Google Developer Student Clubs\",\n      \"company_linkedin_profile_url\": \"https://www.linkedin.com/company/dscjscoe/\",\n      \"description\": null,\n      \"logo_url\": \"https://media.licdn.com/dms/image/C4E0BAQE_1tNZKj3sNQ/company-logo_400_400/0/1617607397103?e=1695254400&v=beta&t=mRveYFhesakeGFzPNIgcgszUrPZ_oo_f-Sl4Kh8VcE0\"\n    },\n    {\n      \"starts_at\": {\n        \"day\": 1,\n        \"month\": 12,\n        \"year\": 2022\n      },\n      \"ends_at\": null,\n      \"title\": \"Say Yes to Life, No to Drugs\",\n      \"cause\": \"HEALTH\",\n      \"company\": \"Ministry Of Home Affairs (mha), GOI\",\n      \"company_linkedin_profile_url\": \"https://www.linkedin.com/company/ministry-of-home-affairs-mha-/\",\n      \"description\": \"\\ud835\\uddd6\\ud835\\uddf2\\ud835\\uddff\\ud835\\ude01\\ud835\\uddf6\\ud835\\uddf3\\ud835\\uddf6\\ud835\\uddf0\\ud835\\uddee\\ud835\\ude01\\ud835\\uddf6\\ud835\\uddfc\\ud835\\uddfb: https://bit.ly/PledgeJatin\",\n      \"logo_url\": \"https://media.licdn.com/dms/image/C4D0BAQE7EcnynUNfOg/company-logo_400_400/0/1659293307400?e=1695254400&v=beta&t=Amf-ZxlrbOJLRO76l0YbenhVzusXhgd6FU0fb-no0FQ\"\n    },\n    {\n      \"starts_at\": {\n        \"day\": 1,\n        \"month\": 10,\n        \"year\": 2021\n      },\n      \"ends_at\": null,\n      \"title\": \"Member\",\n      \"cause\": \"EDUCATION\",\n      \"company\": \"daily.dev\",\n      \"company_linkedin_profile_url\": \"https://www.linkedin.com/company/dailydotdev/\",\n      \"description\": null,\n      \"logo_url\": \"https://media.licdn.com/dms/image/C4E0BAQHPne7VGe_P3A/company-logo_400_400/0/1603704711534?e=1695254400&v=beta&t=e5Gp0lxkjTugd0WkELbkdJmW9VtluwrsnuFZ0EX15Ts\"\n    }\n  ],\n  \"certifications\": [\n    {\n      \"starts_at\": {\n        \"day\": 1,\n        \"month\": 1,\n        \"year\": 2023\n      },\n      \"ends_at\": null,\n      \"name\": \"API Fundamentals Student Expert\",\n      \"license_number\": \"63c1c78cdf42054887f80410\",\n      \"display_source\": \"badgr.io\",\n      \"authority\": \"Postman\",\n      \"url\": \"https://api.badgr.io/public/assertions/ORpisSFbRQWJvkAKJEr8vA\"\n    },\n    {\n      \"starts_at\": {\n        \"day\": 1,\n        \"month\": 1,\n        \"year\": 2023\n      },\n      \"ends_at\": null,\n      \"name\": \"Back End Development and APIs\",\n      \"license_number\": null,\n      \"display_source\": \"freecodecamp.org\",\n      \"authority\": \"freeCodeCamp\",\n      \"url\": \"https://freecodecamp.org/certification/j471n/back-end-development-and-apis\"\n    },\n    {\n      \"starts_at\": {\n        \"day\": 1,\n        \"month\": 1,\n        \"year\": 2023\n      },\n      \"ends_at\": null,\n      \"name\": \"Digital Skills: Artificial Intelligence\",\n      \"license_number\": null,\n      \"display_source\": \"futurelearn.com\",\n      \"authority\": \"Accenture\",\n      \"url\": \"https://www.futurelearn.com/certificates/07o1i90\"\n    },\n    {\n      \"starts_at\": {\n        \"day\": 1,\n        \"month\": 8,\n        \"year\": 2022\n      },\n      \"ends_at\": null,\n      \"name\": \"Developer & Technology Accenture Developer Program\",\n      \"license_number\": \"GXL4zXeSS3kzy7TQR\",\n      \"display_source\": \"google.com\",\n      \"authority\": \"Accenture\",\n      \"url\": \"https://drive.google.com/file/d/1RvReVd4YmRpT2LsnHIX5MWIqxlFTj4bl/view?usp=share_link\"\n    },\n    {\n      \"starts_at\": {\n        \"day\": 1,\n        \"month\": 4,\n        \"year\": 2022\n      },\n      \"ends_at\": null,\n      \"name\": \"JavaScript (Intermediate)\",\n      \"license_number\": \"fde0087a00a7\",\n      \"display_source\": \"hackerrank.com\",\n      \"authority\": \"HackerRank\",\n      \"url\": \"https://www.hackerrank.com/certificates/fde0087a00a7\"\n    },\n    {\n      \"starts_at\": {\n        \"day\": 1,\n        \"month\": 12,\n        \"year\": 2021\n      },\n      \"ends_at\": null,\n      \"name\": \"JavaScript Algorithms and Data Structures\",\n      \"license_number\": null,\n      \"display_source\": \"freecodecamp.org\",\n      \"authority\": \"freeCodeCamp\",\n      \"url\": \"https://freecodecamp.org/certification/j471n/javascript-algorithms-and-data-structures\"\n    },\n    {\n      \"starts_at\": {\n        \"day\": 1,\n        \"month\": 4,\n        \"year\": 2021\n      },\n      \"ends_at\": null,\n      \"name\": \"Python 3 Master Course for 2021\",\n      \"license_number\": \"UC-44d68c58-0203-4835-813a-50b893e01630\",\n      \"display_source\": \"udemy.com\",\n      \"authority\": \"Udemy\",\n      \"url\": \"https://www.udemy.com/certificate/UC-44d68c58-0203-4835-813a-50b893e01630/\"\n    },\n    {\n      \"starts_at\": {\n        \"day\": 1,\n        \"month\": 4,\n        \"year\": 2021\n      },\n      \"ends_at\": null,\n      \"name\": \"Scientific Computing with Python\",\n      \"license_number\": null,\n      \"display_source\": \"freecodecamp.org\",\n      \"authority\": \"freeCodeCamp\",\n      \"url\": \"https://freecodecamp.org/certification/j471n/scientific-computing-with-python-v7\"\n    },\n    {\n      \"starts_at\": {\n        \"day\": 1,\n        \"month\": 12,\n        \"year\": 2020\n      },\n      \"ends_at\": null,\n      \"name\": \"JavaScript (Basic)\",\n      \"license_number\": \"18FF8A46B713\",\n      \"display_source\": \"hackerrank.com\",\n      \"authority\": \"HackerRank\",\n      \"url\": \"https://www.hackerrank.com/certificates/18ff8a46b713\"\n    },\n    {\n      \"starts_at\": {\n        \"day\": 1,\n        \"month\": 11,\n        \"year\": 2020\n      },\n      \"ends_at\": null,\n      \"name\": \"AWSOME DAY Online Conference\",\n      \"license_number\": null,\n      \"display_source\": \"google.com\",\n      \"authority\": \"Amazon Web Services (AWS)\",\n      \"url\": \"https://drive.google.com/file/d/1ApnzbHVMQbLVDZfjZP5u05xjHDw4NDZA/view?usp=shari\"\n    },\n    {\n      \"starts_at\": {\n        \"day\": 1,\n        \"month\": 11,\n        \"year\": 2020\n      },\n      \"ends_at\": null,\n      \"name\": \"Microsoft Al Classroom Series\",\n      \"license_number\": null,\n      \"display_source\": \"google.com\",\n      \"authority\": \"Microsoft\",\n      \"url\": \"https://drive.google.com/file/d/1A8-2thKHG4xenkJzu5Uia6kVPnE0HiAa/view?usp=drivesdk\"\n    },\n    {\n      \"starts_at\": {\n        \"day\": 1,\n        \"month\": 10,\n        \"year\": 2020\n      },\n      \"ends_at\": null,\n      \"name\": \"Become a Software Developer - Learning Path\",\n      \"license_number\": \"AbxzbR6VbLGjkllLsxi8knM-Ypaq\",\n      \"display_source\": \"google.com\",\n      \"authority\": \"LinkedIn\",\n      \"url\": \"https://drive.google.com/file/d/1MXTze2mXB7b8Kod7Pk6Q1BTNb1l0OYn3/view?usp=drivesdk\"\n    },\n    {\n      \"starts_at\": {\n        \"day\": 1,\n        \"month\": 10,\n        \"year\": 2020\n      },\n      \"ends_at\": null,\n      \"name\": \"Learn React Course\",\n      \"license_number\": null,\n      \"display_source\": \"google.com\",\n      \"authority\": \"Codecademy\",\n      \"url\": \"https://drive.google.com/file/d/1TAxq-5pQLUxyW1W9V4ovK53JQL7wtQFf/view?usp=share_link\"\n    },\n    {\n      \"starts_at\": {\n        \"day\": 1,\n        \"month\": 10,\n        \"year\": 2020\n      },\n      \"ends_at\": null,\n      \"name\": \"Web Development\",\n      \"license_number\": null,\n      \"display_source\": \"google.com\",\n      \"authority\": \"Mimo\",\n      \"url\": \"https://drive.google.com/file/d/1HziWP3mLgDozfzXjXcs-qLvTzajU1x4j/view?usp=drivesdk\"\n    },\n    {\n      \"starts_at\": {\n        \"day\": 1,\n        \"month\": 9,\n        \"year\": 2020\n      },\n      \"ends_at\": null,\n      \"name\": \"Learn Python 3 Course\",\n      \"license_number\": null,\n      \"display_source\": \"google.com\",\n      \"authority\": \"Codecademy\",\n      \"url\": \"https://drive.google.com/file/d/1H2znARDQD91Mt8IAC3RPIuuv9oMGkAyB/view?usp=share_link\"\n    },\n    {\n      \"starts_at\": {\n        \"day\": 1,\n        \"month\": 9,\n        \"year\": 2020\n      },\n      \"ends_at\": null,\n      \"name\": \"Learn SQL Course\",\n      \"license_number\": null,\n      \"display_source\": \"google.com\",\n      \"authority\": \"Codecademy\",\n      \"url\": \"https://drive.google.com/file/d/18Eh2IGzdlOJJCAmqaOABAMzzTM8Q3z7F/view?usp=share_link\"\n    },\n    {\n      \"starts_at\": {\n        \"day\": 1,\n        \"month\": 8,\n        \"year\": 2020\n      },\n      \"ends_at\": null,\n      \"name\": \"Learning SQL Programming\",\n      \"license_number\": \"AUu5AHwuiRCKambPS4TfA2L24X0b\",\n      \"display_source\": \"google.com\",\n      \"authority\": \"LinkedIn\",\n      \"url\": \"https://drive.google.com/file/d/1lRinmNHufEOdJc5nJrzyDyrrTJYTKIC4/view?usp=sharing\"\n    },\n    {\n      \"starts_at\": {\n        \"day\": 1,\n        \"month\": 8,\n        \"year\": 2020\n      },\n      \"ends_at\": null,\n      \"name\": \"Python (Basic) \",\n      \"license_number\": \"830BC394DFCA\",\n      \"display_source\": \"hackerrank.com\",\n      \"authority\": \"HackerRank\",\n      \"url\": \"https://www.hackerrank.com/certificates/830bc394dfca\"\n    },\n    {\n      \"starts_at\": {\n        \"day\": 1,\n        \"month\": 7,\n        \"year\": 2020\n      },\n      \"ends_at\": null,\n      \"name\": \"Complete Responsive Web Development: 4 courses in 1\\n\",\n      \"license_number\": null,\n      \"display_source\": \"udemy.com\",\n      \"authority\": \"Udemy\",\n      \"url\": \"https://www.udemy.com/certificate/UC-8e18f7bf-1da1-4e29-b61c-2e75ed88aa29/\"\n    },\n    {\n      \"starts_at\": {\n        \"day\": 1,\n        \"month\": 9,\n        \"year\": 2019\n      },\n      \"ends_at\": null,\n      \"name\": \"C Programming Made Easy\",\n      \"license_number\": null,\n      \"display_source\": \"udemy.com\",\n      \"authority\": \"Udemy\",\n      \"url\": \"https://www.udemy.com/certificate/UC-S7KXIVWQ/\"\n    }\n  ],\n  \"connections\": null,\n  \"people_also_viewed\": [\n    {\n      \"link\": \"https://www.linkedin.com/in/aayushi-rai-2580351a8\",\n      \"name\": \"Aayushi Rai\",\n      \"summary\": null,\n      \"location\": null\n    },\n    {\n      \"link\": \"https://www.linkedin.com/in/iamshadmirza\",\n      \"name\": \"Shad Mirza\",\n      \"summary\": null,\n      \"location\": null\n    },\n    {\n      \"link\": \"https://www.linkedin.com/in/ebenezhar-selvakumar-059559136\",\n      \"name\": \"Ebenezhar Selvakumar\",\n      \"summary\": null,\n      \"location\": null\n    },\n    {\n      \"link\": \"https://www.linkedin.com/in/fazlerocks\",\n      \"name\": \"Syed Fazle Rahman\",\n      \"summary\": null,\n      \"location\": null\n    },\n    {\n      \"link\": \"https://www.linkedin.com/in/radha-dadhich-9284b322a\",\n      \"name\": \"Radha  Dadhich\",\n      \"summary\": null,\n      \"location\": null\n    },\n    {\n      \"link\": \"https://www.linkedin.com/in/savitasri\",\n      \"name\": \"Savita S.\",\n      \"summary\": null,\n      \"location\": null\n    },\n    {\n      \"link\": \"https://www.linkedin.com/in/kush-munot\",\n      \"name\": \"Kush Munot\",\n      \"summary\": null,\n      \"location\": null\n    },\n    {\n      \"link\": \"https://www.linkedin.com/in/prasad-karri-005257210\",\n      \"name\": \"Prasad Karri\",\n      \"summary\": null,\n      \"location\": null\n    },\n    {\n      \"link\": \"https://www.linkedin.com/in/vani-m-kokila-53462315\",\n      \"name\": \"Vani M Kokila\",\n      \"summary\": null,\n      \"location\": null\n    },\n    {\n      \"link\": \"https://www.linkedin.com/in/vaibhav-pansambal-66545a57\",\n      \"name\": \"Vaibhav Pansambal\",\n      \"summary\": null,\n      \"location\": null\n    }\n  ],\n  \"recommendations\": [],\n  \"activities\": [],\n  \"similarly_named_profiles\": [\n    {\n      \"name\": \"Jatin Sharma\",\n      \"link\": \"https://in.linkedin.com/in/jatin-sharma-6aa64445\",\n      \"summary\": \"Lead Operation & Process design safety\",\n      \"location\": \"Ahmedabad\"\n    },\n    {\n      \"name\": \"jatin sharma\",\n      \"link\": \"https://in.linkedin.com/in/jatin-sharma-420318100\",\n      \"summary\": \"Technical Lead\",\n      \"location\": \"West Delhi\"\n    },\n    {\n      \"name\": \"Jatin Sharma\",\n      \"link\": \"https://in.linkedin.com/in/jatin-sharma-a6b59267\",\n      \"summary\": \"Application Development Team Lead at Accenture\",\n      \"location\": \"Shimla\"\n    },\n    {\n      \"name\": \"Jatin Sharma\",\n      \"link\": \"https://in.linkedin.com/in/jatsharma\",\n      \"summary\": \"Software Engineer III @ Stackera | Python | Blockchain | DeFi\",\n      \"location\": \"Greater Delhi Area\"\n    },\n    {\n      \"name\": \"Jatin Sharma\",\n      \"link\": \"https://jp.linkedin.com/in/jatin-sharma-91162090\",\n      \"summary\": \"Data Engineer at Rakuten | Azure Certified DP-203 | (Azure Data Engineer | Azure Data Factory | Databricks | Synapse | Informatica | ETL | Python | Data Engineer | Pyspark | Spark | Hive | Tableau ) | ETL developer\",\n      \"location\": \"Tokyo, Japan\"\n    }\n  ],\n  \"articles\": [],\n  \"groups\": [],\n  \"phone_numbers\": [],\n  \"social_networking_services\": [],\n  \"skills\": [\n    \"Django\",\n    \"Technical Writing\",\n    \"Next.js\",\n    \"Python (Programming Language)\",\n    \"Firebase\",\n    \"React.js\",\n    \"Email Marketing\",\n    \"E-newsletter\",\n    \"Web Content Writing\",\n    \"API Development\",\n    \"API Management\",\n    \"Postman API\",\n    \"Content Writting\",\n    \"Copywriting\",\n    \"SEO Copywriting\",\n    \"JavaScript\",\n    \"TypeScript\",\n    \"Cloud Firestore\",\n    \"C (Programming Language)\",\n    \"Front-End Development\"\n  ],\n  \"inferred_salary\": {\n    \"min\": null,\n    \"max\": null\n  },\n  \"gender\": null,\n  \"birth_date\": null,\n  \"industry\": null,\n  \"extra\": {\n    \"github_profile_id\": null,\n    \"twitter_profile_id\": null,\n    \"facebook_profile_id\": null\n  },\n  \"interests\": [],\n  \"personal_emails\": [],\n  \"personal_numbers\": []\n}"
  },
  {
    "path": "pages/utilities.tsx",
    "content": "import React from \"react\";\nimport Link from \"next/link\";\nimport { motion } from \"framer-motion\";\nimport { FiExternalLink } from \"react-icons/fi\";\nimport MetaData from \"@components/MetaData\";\nimport PageHeader from \"@components/PageHeader\";\nimport utilities from \"@content/utilitiesData\";\nimport pageMeta from \"@content/meta\";\nimport { UtilityType } from \"@lib/types\";\n\nconst containerVariants = {\n  hidden: {},\n  visible: { transition: { staggerChildren: 0.05 } },\n};\n\nconst itemVariants = {\n  hidden: { opacity: 0, y: 10 },\n  visible: {\n    opacity: 1,\n    y: 0,\n    transition: { type: \"spring\" as const, stiffness: 160, damping: 22 },\n  },\n};\n\nexport default function Utilities() {\n  return (\n    <>\n      <MetaData\n        title={pageMeta.utilities.title}\n        description={utilities.description}\n        previewImage={pageMeta.utilities.image}\n        keywords={pageMeta.utilities.keywords}\n      />\n\n      <PageHeader\n        watermark=\"/uses\"\n        eyebrow=\"Setup — 001\"\n        title={utilities.title}\n        description={utilities.description}\n        className=\"pb-32\"\n      >\n        {/* ── Sections ── */}\n        <div className=\"flex flex-col gap-16\">\n          {utilities.data.map((utility, index) => (\n            <UtilitySection key={index} utility={utility} index={index} />\n          ))}\n        </div>\n\n        {/* ── Last updated ── */}\n        <motion.p\n          initial={{ opacity: 0 }}\n          whileInView={{ opacity: 1 }}\n          viewport={{ once: true }}\n          className=\"mt-16 font-mono text-[10px] tracking-[0.35em] uppercase text-gray-400 dark:text-gray-600\"\n        >\n          Last updated — {utilities.lastUpdate}\n        </motion.p>\n      </PageHeader>\n    </>\n  );\n}\n\nfunction UtilitySection({\n  utility,\n  index,\n}: {\n  utility: UtilityType;\n  index: number;\n}) {\n  const num = String(index + 1).padStart(2, \"0\");\n  return (\n    <div>\n      {/* Category header */}\n      <motion.div\n        initial={{ opacity: 0, x: -16 }}\n        whileInView={{ opacity: 1, x: 0 }}\n        viewport={{ once: true }}\n        className=\"flex items-center gap-4 mb-6\"\n      >\n        <span className=\"font-mono text-[10px] tracking-[0.45em] uppercase text-gray-400 dark:text-gray-600\">\n          {num}\n        </span>\n        <div className=\"h-px w-5 bg-gray-300 dark:bg-gray-600\" />\n        <h2 className=\"text-sm font-mono tracking-[0.25em] uppercase text-gray-700 dark:text-gray-300 font-semibold\">\n          {utility.title}\n        </h2>\n        <div className=\"h-px flex-1 bg-gray-200 dark:bg-gray-600\" />\n      </motion.div>\n\n      {/* Tool cards grid */}\n      <motion.div\n        variants={containerVariants}\n        initial=\"hidden\"\n        whileInView=\"visible\"\n        viewport={{ once: true, margin: \"-60px\" }}\n        className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-px bg-gray-200 dark:bg-darkSecondary border border-gray-200 dark:border-neutral-700\"\n      >\n        {utility.data.map((item) => {\n          const Icon = item.Icon;\n          return (\n            <motion.div\n              key={item.name}\n              variants={itemVariants}\n              className=\"h-full\"\n            >\n              <Link\n                href={item.link}\n                target=\"_blank\"\n                rel=\"noopener noreferrer\"\n                title={`${item.name} — ${item.description}`}\n                className=\"group flex items-center gap-4 p-5 h-full bg-white dark:bg-darkPrimary hover:bg-gray-50 dark:hover:bg-darkSecondary transition-colors duration-200\"\n              >\n                <div className=\"flex-shrink-0 w-10 h-10 flex items-center justify-center border border-gray-200 dark:border-gray-700 group-hover:border-gray-400 dark:group-hover:border-gray-500 transition-colors text-gray-700 dark:text-gray-300 group-hover:text-gray-900 dark:group-hover:text-white\">\n                  {/* @ts-ignore */}\n                  <Icon className=\"w-5 h-5\" />\n                </div>\n                <div className=\"flex-1 min-w-0\">\n                  <div className=\"flex items-center justify-between gap-2\">\n                    <span className=\"text-sm font-semibold text-gray-900 dark:text-white group-hover:text-gray-900 dark:group-hover:text-white truncate\">\n                      {item.name}\n                    </span>\n                    <FiExternalLink className=\"w-3.5 h-3.5 flex-shrink-0 text-gray-400 dark:text-gray-600 opacity-0 group-hover:opacity-100 transition-opacity\" />\n                  </div>\n                  <p className=\"text-xs text-gray-500 dark:text-gray-500 mt-0.5 leading-relaxed\">\n                    {item.description}\n                  </p>\n                </div>\n              </Link>\n            </motion.div>\n          );\n        })}\n      </motion.div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "postcss.config.js",
    "content": "module.exports = {\n  plugins: {\n    tailwindcss: {},\n    autoprefixer: {},\n  },\n};\n"
  },
  {
    "path": "public/manifest.json",
    "content": "{\n  \"theme_color\": \"#000\",\n  \"background_color\": \"#fff\",\n  \"display\": \"standalone\",\n  \"scope\": \"/\",\n  \"start_url\": \"/\",\n  \"name\": \"Jatin Sharma\",\n  \"short_name\": \"Jatin Sharma\",\n  \"description\": \"I am currently perusing my Bachelor Degree in Computer Science. I can code in Python, C, C++, etc. I also work on React & Next.js. This is my portfolio which you can install as Progressive Web app.\",\n  \"icons\": [\n    {\n      \"src\": \"icons/maskable_icon_x192.png\",\n      \"sizes\": \"192x192\",\n      \"type\": \"image/x-icon\",\n      \"purpose\": \"maskable\"\n    },\n    {\n      \"src\": \"icons/icon-48x48.png\",\n      \"sizes\": \"48x48\",\n      \"type\": \"image/png\"\n    },\n    {\n      \"src\": \"icons/icon-72x72.png\",\n      \"sizes\": \"72x72\",\n      \"type\": \"image/png\"\n    },\n    {\n      \"src\": \"icons/icon-96x96.png\",\n      \"sizes\": \"96x96\",\n      \"type\": \"image/png\"\n    },\n    {\n      \"src\": \"icons/icon-128x128.png\",\n      \"sizes\": \"128x128\",\n      \"type\": \"image/png\"\n    },\n    {\n      \"src\": \"icons/icon-144x144.png\",\n      \"sizes\": \"144x144\",\n      \"type\": \"image/png\"\n    },\n    {\n      \"src\": \"icons/icon-152x152.png\",\n      \"sizes\": \"152x152\",\n      \"type\": \"image/png\"\n    },\n    {\n      \"src\": \"icons/icon-192x192.png\",\n      \"sizes\": \"192x192\",\n      \"type\": \"image/png\"\n    },\n    {\n      \"src\": \"icons/icon-512x512.png\",\n      \"sizes\": \"512x512\",\n      \"type\": \"image/png\"\n    }\n  ],\n  \"screenshots\": [\n    {\n      \"src\": \"screenshots/home.png\",\n      \"sizes\": \"411x821\",\n      \"type\": \"image/gif\"\n    },\n    {\n      \"src\": \"screenshots/blogs.png\",\n      \"sizes\": \"411x821\",\n      \"type\": \"image/gif\"\n    },\n    {\n      \"src\": \"screenshots/projects.png\",\n      \"sizes\": \"411x821\",\n      \"type\": \"image/gif\"\n    },\n    {\n      \"src\": \"screenshots/stats.png\",\n      \"sizes\": \"411x821\",\n      \"type\": \"image/gif\"\n    },\n    {\n      \"src\": \"screenshots/utilities.png\",\n      \"sizes\": \"411x821\",\n      \"type\": \"image/gif\"\n    },\n    {\n      \"src\": \"screenshots/contact.png\",\n      \"sizes\": \"411x821\",\n      \"type\": \"image/gif\"\n    }\n  ],\n  \"shortcuts\": [\n    {\n      \"name\": \"Blogs\",\n      \"url\": \"/blogs\",\n      \"icons\": [\n        {\n          \"src\": \"shortcuts/blog.png\",\n          \"sizes\": \"202x202\",\n          \"type\": \"image/png\",\n          \"purpose\": \"any\"\n        }\n      ]\n    },\n    {\n      \"name\": \"About me\",\n      \"url\": \"/about\",\n      \"icons\": [\n        {\n          \"src\": \"shortcuts/about.png\",\n          \"sizes\": \"202x202\",\n          \"type\": \"image/png\",\n          \"purpose\": \"any\"\n        }\n      ]\n    },\n    {\n      \"name\": \"Newsletter\",\n      \"url\": \"/newsletter\",\n      \"icons\": [\n        {\n          \"src\": \"shortcuts/newsletter.png\",\n          \"sizes\": \"202x202\",\n          \"type\": \"image/png\",\n          \"purpose\": \"any\"\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "public/robots.txt",
    "content": "User-agent: *\nSitemap: https://jatin.vercel.app/sitemap.xml"
  },
  {
    "path": "sanity/.eslintrc",
    "content": "{\n  \"extends\": \"@sanity/eslint-config-studio\"\n}\n"
  },
  {
    "path": "sanity/.gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# Dependencies\n/node_modules\n/.pnp\n.pnp.js\n\n# Compiled Sanity Studio\n/dist\n\n# Temporary Sanity runtime, generated by the CLI on every dev server start\n/.sanity\n\n# Logs\n/logs\n*.log\n\n# Coverage directory used by testing tools\n/coverage\n\n# Misc\n.DS_Store\n*.pem\n\n# Typescript\n*.tsbuildinfo\n\n# Dotenv and similar local-only files\n*.local\n"
  },
  {
    "path": "sanity/README.md",
    "content": "# Sanity Blogging Content Studio\n\nCongratulations, you have now installed the Sanity Content Studio, an open source real-time content editing environment connected to the Sanity backend.\n\nNow you can do the following things:\n\n- [Read “getting started” in the docs](https://www.sanity.io/docs/introduction/getting-started?utm_source=readme)\n- Check out the example frontend: [React/Next.js](https://github.com/sanity-io/tutorial-sanity-blog-react-next)\n- [Read the blog post about this template](https://www.sanity.io/blog/build-your-own-blog-with-sanity-and-next-js?utm_source=readme)\n- [Join the community Slack](https://slack.sanity.io/?utm_source=readme)\n- [Extend and build plugins](https://www.sanity.io/docs/content-studio/extending?utm_source=readme)\n"
  },
  {
    "path": "sanity/gist.md.ndjson",
    "content": "{\"_type\":\"post\", \"title\":\"ruby rails\",\"_createdAt\":\"2023-05-26T21:06:53Z\",\"publishedAt\":\"2023-05-26T21:06:53Z\",\"body\":[{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Ruby is a programming language that is often praised for its simplicity, elegance, and expressiveness. Just like any other programming language, it's important to focus on writing good quality code to ensure that the applications we build are reliable, scalable, and easy to maintain. Writing high-quality, maintainable Ruby code is essential for developing applications. Performing regular code reviews and using code quality tools can help catch problems early and improve your code over time. This article explores some of the best options for Ruby code reviews and quality analysis.\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"h2\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Introduction to Ruby on Rails\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"image\",\"asset\":{\"src\":\"https://aldibs.com/wp-content/uploads/2020/01/ruby-on-rails-development.jpg\",\"alt\":\"Differences between Ruby and Ruby on Rails - Aldibs Software Solutions\"}}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Ruby on Rails, or simply Rails, is an open-source web application framework written in Ruby. It is designed to make programming web applications easier and faster.\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"The main benefits of using Rails for building web applications are:\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[\"strong\"],\"text\":\"Productivity\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Rails make developers very productive through conventions, generators, migrations and other tools. Features can be built quickly.\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[\"strong\"],\"text\":\"Convention over configuration\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Rails has opinions about the best way to do things and configure itself by default. This means less time spent configuring and more time writing code.\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[\"strong\"],\"text\":\"Elegant design\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Rails enforces an elegant object-oriented design that minimizes repetition and coupling through the use of DRY principles.\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[\"strong\"],\"text\":\"Simple syntax\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Ruby's syntax is clean and straightforward, making Rails code readable and enjoyable to write.\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[\"strong\"],\"text\":\"MVC architecture\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Rails embraces and implements the MVC pattern cleanly, separating application concerns into models, views and controllers.\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[\"strong\"],\"text\":\"Support for testing\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Rails makes it easy to write automated tests, allowing developers to refactor code quickly and safely.\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"h2\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Importance of Code Review in Ruby on Rails Development\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Code reviews are an important practice in software development that can:\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"h3\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Maintain code quality\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"By having another set of eyes review the code, issues like inefficient algorithms, poor variable names, unnecessary complexity, and bad practices can be caught and corrected before the code is merged. This helps maintain a consistent code style and quality standard.\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"h3\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Improve collaboration\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"The code review process involves a discussion between reviewers and authors, sharing knowledge and best practices. This fosters communication and collaboration between team members.\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"image\",\"asset\":{\"src\":\"https://mobisoftinfotech.com/resources/wp-content/uploads/2022/01/og-code-quality.png\",\"alt\":\"Best Practices to Ensure Better Code Quality of Your Software in 2023\"}}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"h3\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Reduce bugs\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Catching bugs early during code reviews is much cheaper and faster than fixing them later after the code has been merged and released. This significantly improves software reliability.\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"image\",\"asset\":{\"src\":\"https://www.bigscal.com/wp-content/uploads/2022/08/6-Proven-Tips-To-Prevent-Software-Bugs-For-Developers.png\",\"alt\":\"6 Proven Tips To Prevent Software Bugs For Developers\"}}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"h3\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Opportunities for refactoring\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Reviewers may spot opportunities to refactor or restructure code in a more maintainable way. This can improve the design and architecture of the codebase over time.\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"image\",\"asset\":{\"src\":\"https://media.geeksforgeeks.org/wp-content/cdn-uploads/20200922214720/Red-Green-Refactoring.png\",\"alt\":\"7 Code Refactoring Techniques in Software Engineering - GeeksforGeeks\"}}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"h3\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Share knowledge\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Junior developers can learn good coding practices and design patterns by reviewing the code of more experienced developers, and vice versa.\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"image\",\"asset\":{\"src\":\"https://asphn.org/wp-content/uploads/2019/12/encouraging-teamwork-through-knowledge-sharing.jpg\",\"alt\":\"Encouraging Teamwork Through Knowledge Sharing - ASPHN\"}}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"h3\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Performance issues are identified\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Reviewers may spot performance bottlenecks, inefficient algorithms or resource usage issues that impact performance. These can be addressed before the code is merged.\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"image\",\"asset\":{\"src\":\"https://www.searchenginejournal.com/wp-content/uploads/2022/10/website-performance-and-health-monitoring-635945792a855-sej.png\",\"alt\":\"Website Performance & Health Monitoring: Tips & Best Practices\"}}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"h2\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Common Challenges in Ruby on Rails Code\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"When working with Ruby on Rails code, developers often face various challenges that can impact the overall quality of their applications. These challenges can range from performance bottlenecks and scalability concerns to security vulnerabilities. Addressing these common challenges is crucial.\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Here are some common challenges developers face when working with Ruby on Rails code:\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"h3\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Performance bottlenecks\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Performance bottlenecks in Ruby on Rails occur when certain parts of the code or system architecture significantly slow down the application's performance. Let's consider an example to illustrate this:\"}]},{\"_type\":\"code\",\"code\":\"# Slow query due to inefficient use of ActiveRecord @users = User.all @users.each do |user| user.orders.each do |order| # Perform some calculations or operations on each order end end # Optimized query @users = User.includes(:orders) @users.each do |user| user.orders.each do |order| # Perform some calculations or operations on each order end end \",\"language\":\"ruby\"},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"The above-unoptimized code, makes a separate database query to fetch each user's orders, resulting in N + 1 queries total where N is the number of users.\"}]},{\"_type\":\"block\",\"markDefs\":[{\"_key\":\"6629c84eb71f\",\"_type\":\"link\",\"href\":\"https://www.allerin.com/blog/eager-loading-inwith-rails\"}],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"The optimized code uses \"},{\"_type\":\"span\",\"marks\":[\"6629c84eb71f\"],\"text\":\"eager loading\"},{\"_type\":\"span\",\"marks\":[],\"text\":\" to fetch all user data and all order data in 2 queries. This reduces the number of queries from N + 1 to a constant number, improving performance.\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"h3\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Scalability concerns\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"As traffic and data volumes increase, Rails applications can struggle to scale vertically on a single server. Scaling horizontally across multiple servers and databases requires additional configuration and tooling.\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"h3\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Security vulnerabilities\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Like any framework, Rails has had security vulnerabilities over the years that require patching. Developers must keep dependencies up to date and follow secure coding practices.\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"h3\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Code readability problems\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Rails aims for convention over configuration, but this can result in \\\"magic\\\" that makes the code less readable for new developers. Over time, the codebase can become cluttered and difficult to navigate.\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"h3\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Dependency management\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Rails has a large number of dependencies that must be managed and kept up to date. Outdated dependencies can introduce security vulnerabilities or cause compatibility issues.\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"h3\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Database migration issues\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Rails uses Migrations to manage database schema changes, but over time these can become complex and difficult to maintain. Schema refactoring may be required.\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"h2\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Tools for Ruby on Rails Code Review\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"h3\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"RuboCop\"}]},{\"_type\":\"block\",\"markDefs\":[{\"_key\":\"74f88b388894\",\"_type\":\"link\",\"href\":\"https://docs.rubocop.org/\"},{\"_key\":\"54532bf690d6\",\"_type\":\"link\",\"href\":\"https://rubystyle.guide/\"}],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[\"74f88b388894\"],\"text\":\"RuboCop\"},{\"_type\":\"span\",\"marks\":[],\"text\":\" is a Ruby static code analyzer (a.k.a. \"},{\"_type\":\"span\",\"marks\":[\"code\"],\"text\":\"linter\"},{\"_type\":\"span\",\"marks\":[],\"text\":\") and code formatter. Out of the box it will enforce many of the guidelines outlined in the community \"},{\"_type\":\"span\",\"marks\":[\"54532bf690d6\"],\"text\":\"Ruby Style Guide\"},{\"_type\":\"span\",\"marks\":[],\"text\":\". Apart from reporting the problems discovered in your code, RuboCop can also automatically fix many of them for you.\"}]},{\"_type\":\"code\",\"code\":\"rubocop # Check entire project rubocop --auto-correct # Automatically correct offenses rubocop --only Rails/ActionFilter # Check specific cop \",\"language\":\"bash\"},{\"_type\":\"block\",\"markDefs\":[{\"_key\":\"89af21b60def\",\"_type\":\"link\",\"href\":\"https://www.youtube.com/watch?v=sfOGjcMVQ9U\"}],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"{% youtube \"},{\"_type\":\"span\",\"marks\":[\"89af21b60def\"],\"text\":\"https://www.youtube.com/watch?v=sfOGjcMVQ9U\"},{\"_type\":\"span\",\"marks\":[],\"text\":\" %}\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"h3\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Reek\"}]},{\"_type\":\"block\",\"markDefs\":[{\"_key\":\"4263f2a002fc\",\"_type\":\"link\",\"href\":\"https://github.com/troessner/reek\"}],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[\"4263f2a002fc\"],\"text\":\"Reek\"},{\"_type\":\"span\",\"marks\":[],\"text\":\" is a code smell detection tool for Ruby that helps identify potential design issues. It analyzes your codebase and provides feedback on areas that might benefit from refactoring or improvement. Here's an overview of what Reek is and how to use it:\"}]},{\"_type\":\"code\",\"code\":\"reek app/ # Check whole app directory reek -c reek.yml app/models/*.rb # Check models with config \",\"language\":\"bash\"},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"image\",\"asset\":{\"src\":\"https://opengraph.githubassets.com/187baac8e516274a2e32a4628ec25f8c7dc2a032e5148d702dfb3032f15fb15e/troessner/reek\",\"alt\":\"GitHub - troessner/reek: Code smell detector for Ruby\"}}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"h3\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Brakeman\"}]},{\"_type\":\"block\",\"markDefs\":[{\"_key\":\"e991fa49d846\",\"_type\":\"link\",\"href\":\"https://brakemanscanner.org/\"}],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[\"e991fa49d846\"],\"text\":\"Brakeman\"},{\"_type\":\"span\",\"marks\":[],\"text\":\" is a static analysis security vulnerability scanner for Ruby on Rails applications. It finds potential security issues in Rails applications by examining the Ruby code. Brakeman helps find and fix security holes before deploying your Rails app.\"}]},{\"_type\":\"code\",\"code\":\"brakeman # To run brakeman brakeman your_rails_app # Add path of the folder brakeman -A # To run all the checks brakeman -q # To suppress informational warning \",\"language\":\"bash\"},{\"_type\":\"block\",\"markDefs\":[{\"_key\":\"7687fc3fab29\",\"_type\":\"link\",\"href\":\"https://youtu.be/DHHHnPwSY5I\"}],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"{% youtube \"},{\"_type\":\"span\",\"marks\":[\"7687fc3fab29\"],\"text\":\"https://youtu.be/DHHHnPwSY5I\"},{\"_type\":\"span\",\"marks\":[],\"text\":\" %}\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"h3\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"RSpec\"}]},{\"_type\":\"block\",\"markDefs\":[{\"_key\":\"73d2397ddd27\",\"_type\":\"link\",\"href\":\"https://rspec.info/\"},{\"_key\":\"2da17a72c7d0\",\"_type\":\"link\",\"href\":\"https://en.wikipedia.org/wiki/Behavior-driven_development\"}],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[\"73d2397ddd27\"],\"text\":\"RSpec\"},{\"_type\":\"span\",\"marks\":[],\"text\":\" is a testing framework for Ruby that is widely used in the Ruby on Rails community. It allows developers to write and execute automated tests. RSpec promotes \"},{\"_type\":\"span\",\"marks\":[\"2da17a72c7d0\"],\"text\":\"behavior-driven development\"},{\"_type\":\"span\",\"marks\":[],\"text\":\" (BDD) by providing a readable syntax for describing the expected behavior of the application.\"}]},{\"_type\":\"code\",\"code\":\"bundle exec rspec # Run all spec files bundle exec rspec -fd # Run specs with failures/pending bundle exec rspec path/to/spec.rb # Run specific spec \",\"language\":\"bash\"},{\"_type\":\"block\",\"markDefs\":[{\"_key\":\"cfa7efa76f0d\",\"_type\":\"link\",\"href\":\"https://www.youtube.com/watch?v=-uhFA74eBG0\"}],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"{% youtube \"},{\"_type\":\"span\",\"marks\":[\"cfa7efa76f0d\"],\"text\":\"https://www.youtube.com/watch?v=-uhFA74eBG0\"},{\"_type\":\"span\",\"marks\":[],\"text\":\" %}\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"h3\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Flay\"}]},{\"_type\":\"block\",\"markDefs\":[{\"_key\":\"5cd38947563e\",\"_type\":\"link\",\"href\":\"https://github.com/seattlerb/flay\"}],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[\"5cd38947563e\"],\"text\":\"Flay\"},{\"_type\":\"span\",\"marks\":[],\"text\":\" analyzes ruby code for structural similarities. Differences in literal values, names, whitespace, and programming style are all ignored. Flay helps reduce code duplication and keep your code DRY (Don't Repeat Yourself).\"}]},{\"_type\":\"code\",\"code\":\"flay . # Analyzes the code within the current directory flay --diff . # Shows a side-by-side diff of the duplicated code \",\"language\":\"bash\"},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"image\",\"asset\":{\"src\":\"https://ik.imagekit.io/analysistools/flay_https_3A_2F_2Fruby.sadi.st_2FFlay.html_rS3Inq4vnl.jpg\",\"alt\":\"flay, a linter for Ruby - Rating And 47 Alternatives | Analysis Tools\"}}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"h3\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"Wrapping up\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"In conclusion, code reviews are essential for high-quality Ruby on Rails applications. They catch issues early, improve consistency and transfer knowledge between developers. Though time-consuming initially, code reviews save much more time by reducing bugs and improving maintainability. Start small with partial code reviews and expand coverage over time as the team adapts. The benefits to code quality, productivity and sustainability make code reviews a best practice for any Rails development process. Implementing regular code reviews should be a top priority for any team-building.\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"If you want more articles on similar topics just let me know in the comments section. And don't forget to ❤️ the article. I'll see you in the next one. In the meantime you can follow me here:\"}]},{\"_type\":\"block\",\"markDefs\":[],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"{% user j471n %}\"}]},{\"_type\":\"block\",\"markDefs\":[{\"_key\":\"ce4c547cffd6\",\"_type\":\"link\",\"href\":\"https://res.cloudinary.com/practicaldev/image/fetch/s--haJKPmN9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://media0.giphy.com/media/65ODDoEMHM5wbGDHWT/giphy.gif%3Fcid%3Decf05e472rut0ytp29eznos4focko84kmq33u4btpu74cw1w%26rid%3Dgiphy.gif\"}],\"style\":\"normal\",\"children\":[{\"_type\":\"span\",\"marks\":[],\"text\":\"![](\"},{\"_type\":\"span\",\"marks\":[\"ce4c547cffd6\"],\"text\":\"https://res.cloudinary.com/practicaldev/image/fetch/s--haJKPmN9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://media0.giphy.com/media/65ODDoEMHM5wbGDHWT/giphy.gif%3Fcid%3Decf05e472rut0ytp29eznos4focko84kmq33u4btpu74cw1w%26rid%3Dgiphy.gif\"},{\"_type\":\"span\",\"marks\":[],\"text\":\" align=\\\"center\\\")\"}]}]}\n"
  },
  {
    "path": "sanity/package.json",
    "content": "{\n  \"name\": \"j471n-blog\",\n  \"private\": true,\n  \"version\": \"1.0.0\",\n  \"main\": \"package.json\",\n  \"license\": \"UNLICENSED\",\n  \"scripts\": {\n    \"dev\": \"sanity dev\",\n    \"start\": \"sanity start\",\n    \"build\": \"sanity build\",\n    \"deploy\": \"sanity deploy\",\n    \"deploy-graphql\": \"sanity graphql deploy\"\n  },\n  \"keywords\": [\n    \"sanity\"\n  ],\n  \"dependencies\": {\n    \"@dnd-kit/sortable\": \"^7.0.2\",\n    \"@sanity/vision\": \"^3.12.2\",\n    \"dotenv\": \"^16.3.1\",\n    \"react\": \"^18.2.0\",\n    \"react-dom\": \"^18.2.0\",\n    \"react-is\": \"^18.2.0\",\n    \"sanity\": \"^3.12.2\",\n    \"styled-components\": \"^5.3.9\"\n  },\n  \"devDependencies\": {\n    \"@sanity/eslint-config-studio\": \"^2.0.1\",\n    \"@types/react\": \"^18.0.25\",\n    \"@types/styled-components\": \"^5.1.26\",\n    \"eslint\": \"^8.6.0\",\n    \"prettier\": \"^2.8.8\",\n    \"typescript\": \"^4.9.5\"\n  },\n  \"prettier\": {\n    \"semi\": false,\n    \"printWidth\": 100,\n    \"bracketSpacing\": false,\n    \"singleQuote\": true\n  }\n}\n"
  },
  {
    "path": "sanity/sanity.cli.ts",
    "content": "import {defineCliConfig} from '@sanity/cli'\n\nexport default defineCliConfig({\n  api: {\n    projectId: '5ec749u0',\n    dataset: 'production',\n  },\n})\n"
  },
  {
    "path": "sanity/sanity.config.ts",
    "content": "// import {defineConfig} from 'sanity/lib/exports' // ONLY USE WHILE BUILDING THE APP\n\nimport {defineConfig} from 'sanity' // <==== USE THIS FOR LOCAL\nimport {deskTool} from 'sanity/desk'\nimport {schemaTypes} from './schemas'\nimport {visionTool} from '@sanity/vision'\n\nexport default defineConfig({\n  name: 'default',\n  title: 'j471n-blog',\n\n  projectId: '5ec749u0',\n  dataset: 'production',\n\n  plugins: [deskTool(), visionTool()],\n\n  schema: {\n    types: schemaTypes,\n  },\n})\n"
  },
  {
    "path": "sanity/schemas/author.ts",
    "content": "import {defineField, defineType} from 'sanity'\n\nexport default defineType({\n  name: 'author',\n  title: 'Author',\n  type: 'document',\n  fields: [\n    defineField({\n      name: 'name',\n      title: 'Name',\n      type: 'string',\n    }),\n    defineField({\n      name: 'slug',\n      title: 'Slug',\n      type: 'slug',\n      options: {\n        source: 'name',\n        maxLength: 96,\n      },\n    }),\n    defineField({\n      name: 'image',\n      title: 'Image',\n      type: 'image',\n      options: {\n        hotspot: true,\n      },\n    }),\n    defineField({\n      name: 'bio',\n      title: 'Bio',\n      type: 'array',\n      of: [\n        {\n          title: 'Block',\n          type: 'block',\n          styles: [{title: 'Normal', value: 'normal'}],\n          lists: [],\n        },\n      ],\n    }),\n  ],\n  preview: {\n    select: {\n      title: 'name',\n      media: 'image',\n    },\n  },\n})\n"
  },
  {
    "path": "sanity/schemas/blockContent.ts",
    "content": "import {defineType, defineArrayMember} from 'sanity'\n\n/**\n * This is the schema definition for the rich text fields used for\n * for this blog studio. When you import it in schemas.js it can be\n * reused in other parts of the studio with:\n *  {\n *    name: 'someName',\n *    title: 'Some title',\n *    type: 'blockContent'\n *  }\n */\nexport default defineType({\n  title: 'Block Content',\n  name: 'blockContent',\n  type: 'array',\n  of: [\n    defineArrayMember({\n      title: 'Block',\n      type: 'block',\n      // Styles let you set what your user can mark up blocks with. These\n      // correspond with HTML tags, but you can set any title or value\n      // you want and decide how you want to deal with it where you want to\n      // use your content.\n      styles: [\n        {title: 'Normal', value: 'normal'},\n        {title: 'H1', value: 'h1'},\n        {title: 'H2', value: 'h2'},\n        {title: 'H3', value: 'h3'},\n        {title: 'H4', value: 'h4'},\n        {title: 'Quote', value: 'blockquote'},\n      ],\n      lists: [{title: 'Bullet', value: 'bullet'}],\n      // Marks let you mark up inline text in the block editor.\n      marks: {\n        // Decorators usually describe a single property – e.g. a typographic\n        // preference or highlighting by editors.\n        decorators: [\n          {title: 'Strong', value: 'strong'},\n          {title: 'Emphasis', value: 'em'},\n        ],\n        // Annotations can be any object structure – e.g. a link or a footnote.\n        annotations: [\n          {\n            title: 'URL',\n            name: 'link',\n            type: 'object',\n            fields: [\n              {\n                title: 'URL',\n                name: 'href',\n                type: 'url',\n              },\n            ],\n          },\n        ],\n      },\n    }),\n    // You can add additional types here. Note that you can't use\n    // primitive types such as 'string' and 'number' in the same array\n    // as a block type.\n    defineArrayMember({\n      type: 'image',\n      options: {hotspot: true},\n    }),\n  ],\n})\n"
  },
  {
    "path": "sanity/schemas/category.ts",
    "content": "import {defineField, defineType} from 'sanity'\n\nexport default defineType({\n  name: 'category',\n  title: 'Category',\n  type: 'document',\n  fields: [\n    defineField({\n      name: 'title',\n      title: 'Title',\n      type: 'string',\n    }),\n    defineField({\n      name: 'description',\n      title: 'Description',\n      type: 'text',\n    }),\n  ],\n})\n"
  },
  {
    "path": "sanity/schemas/epigraph.ts",
    "content": "import {defineField, defineType, defineArrayMember} from 'sanity'\n\nconst SOURCE_TYPES = [\n  {title: 'Book', value: 'book'},\n  {title: 'Movie', value: 'movie'},\n  {title: 'TV Show', value: 'tvShow'},\n  {title: 'Person', value: 'person'},\n  {title: 'Song / Lyrics', value: 'song'},\n  {title: 'Podcast', value: 'podcast'},\n  {title: 'Other', value: 'other'},\n]\n\nexport default defineType({\n  name: 'epigraph',\n  title: 'Epigraph',\n  type: 'document',\n  fields: [\n    defineField({\n      name: 'quote',\n      title: 'Quote / Passage',\n      type: 'text',\n      description: 'The quote, stanza, or passage you want to preserve.',\n      validation: (Rule) => Rule.required(),\n    }),\n    defineField({\n      name: 'sourceType',\n      title: 'Source Type',\n      type: 'string',\n      options: {\n        list: SOURCE_TYPES,\n        layout: 'radio',\n      },\n      validation: (Rule) => Rule.required(),\n    }),\n    defineField({\n      name: 'sourceTitle',\n      title: 'Source Title',\n      type: 'string',\n      description: 'Book name, movie title, show name, song name, podcast name, or person name.',\n      validation: (Rule) => Rule.required(),\n    }),\n    defineField({\n      name: 'sourceMeta',\n      title: 'Source Meta',\n      type: 'string',\n      description:\n        'Author (book), artist (song), director (movie), host (podcast), context (person), etc.',\n    }),\n    defineField({\n      name: 'speaker',\n      title: 'Speaker / Character',\n      type: 'string',\n      description:\n        'Character or person who said it (e.g. character in a book/movie, the person themselves).',\n    }),\n    defineField({\n      name: 'year',\n      title: 'Year',\n      type: 'number',\n      description: 'Release or publication year (optional).',\n    }),\n    {\n      name: 'tags',\n      title: 'Tags',\n      type: 'array' as const,\n      of: [{type: 'string' as const}],\n      options: {\n        layout: 'tags',\n      },\n      description: 'e.g. \"life\", \"motivation\", \"love\", \"philosophy\"',\n    },\n    defineField({\n      name: 'addedAt',\n      title: 'Added At',\n      type: 'datetime',\n      initialValue: () => new Date().toISOString(),\n    }),\n  ],\n\n  preview: {\n    select: {\n      title: 'quote',\n      subtitle: 'sourceTitle',\n      sourceType: 'sourceType',\n    },\n    prepare({title, subtitle, sourceType}) {\n      const label = SOURCE_TYPES.find((s) => s.value === sourceType)?.title ?? sourceType\n      return {\n        title: title ? (title.length > 80 ? title.slice(0, 80) + '…' : title) : 'Untitled',\n        subtitle: subtitle ? `${label} — ${subtitle}` : label,\n      }\n    },\n  },\n})\n"
  },
  {
    "path": "sanity/schemas/index.ts",
    "content": "import author from './author'\nimport blockContent from './blockContent'\nimport category from './category'\nimport language from './language'\nimport epigraph from './epigraph'\nimport organization from './organization'\nimport post from './post'\nimport snippet from './snippet'\nimport static_page from './static_page'\n\nexport const schemaTypes = [\n  post,\n  author,\n  category,\n  blockContent,\n  organization,\n  snippet,\n  language,\n  static_page,\n  epigraph,\n]\n"
  },
  {
    "path": "sanity/schemas/language.ts",
    "content": "import {defineField, defineType} from 'sanity'\n\nexport default defineType({\n  name: 'language',\n  title: 'Language',\n  type: 'document',\n  fields: [\n    defineField({\n      name: 'name',\n      title: 'Name',\n      type: 'string',\n    }),\n    defineField({\n      name: 'slug',\n      title: 'Slug',\n      type: 'slug',\n      options: {\n        source: 'name',\n        maxLength: 96,\n      },\n    }),\n    defineField({\n      name: 'image',\n      title: 'Image',\n      type: 'image',\n      options: {\n        hotspot: true,\n      },\n    }),\n  ],\n  preview: {\n    select: {\n      title: 'name',\n      media: 'image',\n    },\n  },\n})\n"
  },
  {
    "path": "sanity/schemas/organization.ts",
    "content": "import {defineField, defineType} from 'sanity'\n\nexport default defineType({\n  name: 'organization',\n  title: 'Organization',\n  type: 'document',\n  fields: [\n    defineField({\n      name: 'name',\n      title: 'Name',\n      type: 'string',\n    }),\n    defineField({\n      name: 'slug',\n      title: 'Slug',\n      type: 'slug',\n      options: {\n        source: 'name',\n        maxLength: 96,\n      },\n    }),\n    defineField({\n      name: 'website',\n      title: 'Website',\n      type: 'string',\n    }),\n    defineField({\n      name: 'image',\n      title: 'Image',\n      type: 'image',\n      options: {\n        hotspot: true,\n      },\n    }),\n    defineField({\n      name: 'bio',\n      title: 'Bio',\n      type: 'array',\n      of: [\n        {\n          title: 'Block',\n          type: 'block',\n          styles: [{title: 'Normal', value: 'normal'}],\n          lists: [],\n        },\n      ],\n    }),\n  ],\n  preview: {\n    select: {\n      title: 'name',\n      media: 'image',\n    },\n  },\n})\n"
  },
  {
    "path": "sanity/schemas/post.ts",
    "content": "import {defineField, defineType} from 'sanity'\n\nexport default defineType({\n  name: 'post',\n  title: 'Post',\n  type: 'document',\n  fields: [\n    defineField({\n      name: 'title',\n      title: 'Title',\n      type: 'string',\n    }),\n    defineField({\n      name: 'slug',\n      title: 'Slug',\n      type: 'slug',\n      options: {\n        source: 'title',\n        maxLength: 96,\n      },\n    }),\n    defineField({\n      name: 'keywords',\n      title: 'Keywords',\n      type: 'string',\n    }),\n    defineField({\n      name: 'excerpt',\n      title: 'Excerpt',\n      type: 'text',\n    }),\n\n    defineField({\n      name: 'author',\n      title: 'Author',\n      type: 'reference',\n      to: {type: 'author'},\n    }),\n    defineField({\n      name: 'organization',\n      title: 'Organization',\n      type: 'reference',\n      to: {type: 'organization'},\n    }),\n    defineField({\n      name: 'mainImage',\n      title: 'Main image',\n      type: 'image',\n      options: {\n        hotspot: true,\n      },\n    }),\n    defineField({\n      name: 'categories',\n      title: 'Categories',\n      type: 'array',\n      of: [{type: 'reference', to: {type: 'category'}}],\n    }),\n    defineField({\n      name: 'publishedAt',\n      title: 'Published at',\n      type: 'datetime',\n    }),\n    defineField({\n      name: 'content',\n      title: 'Content',\n      type: 'text',\n    }),\n  ],\n\n  preview: {\n    select: {\n      title: 'title',\n      author: 'author.name',\n      media: 'mainImage',\n    },\n    prepare(selection) {\n      const {author} = selection\n      return {...selection, subtitle: author && `by ${author}`}\n    },\n  },\n})\n"
  },
  {
    "path": "sanity/schemas/snippet.ts",
    "content": "import {defineField, defineType} from 'sanity'\n\nexport default defineType({\n  name: 'snippet',\n  title: 'Snippet',\n  type: 'document',\n  fields: [\n    defineField({\n      name: 'title',\n      title: 'Title',\n      type: 'string',\n    }),\n    defineField({\n      name: 'slug',\n      title: 'Slug',\n      type: 'slug',\n      options: {\n        source: 'title',\n        maxLength: 96,\n      },\n    }),\n    defineField({\n      name: 'excerpt',\n      title: 'Excerpt',\n      type: 'text',\n    }),\n\n    defineField({\n      name: 'language',\n      title: 'Language',\n      type: 'reference',\n      to: {type: 'language'},\n    }),\n    defineField({\n      name: 'publishedAt',\n      title: 'Published at',\n      type: 'datetime',\n    }),\n    defineField({\n      name: 'content',\n      title: 'Content',\n      type: 'text',\n    }),\n  ],\n\n  preview: {\n    select: {\n      title: 'title',\n      language: 'language.name',\n      media: 'language.image',\n    },\n    prepare(selection) {\n      const {language} = selection\n      return {...selection, subtitle: language}\n    },\n  },\n})\n"
  },
  {
    "path": "sanity/schemas/static_page.ts",
    "content": "import {defineField, defineType} from 'sanity'\n\nexport default defineType({\n  name: 'static_page',\n  title: 'Static Page',\n  type: 'document',\n  fields: [\n    defineField({\n      name: 'title',\n      title: 'Title',\n      type: 'string',\n    }),\n    defineField({\n      name: 'slug',\n      title: 'Slug',\n      type: 'slug',\n      options: {\n        source: 'title',\n        maxLength: 96,\n      },\n    }),\n    defineField({\n      name: 'keywords',\n      title: 'Keywords',\n      type: 'string',\n    }),\n    defineField({\n      name: 'excerpt',\n      title: 'Excerpt',\n      type: 'text',\n    }),\n    defineField({\n      name: 'mainImage',\n      title: 'Main image',\n      type: 'image',\n      options: {\n        hotspot: true,\n      },\n    }),\n    defineField({\n      name: 'publishedAt',\n      title: 'Published at',\n      type: 'datetime',\n    }),\n    defineField({\n      name: 'content',\n      title: 'Content',\n      type: 'text',\n    }),\n  ],\n\n  preview: {\n    select: {\n      title: 'title',\n      media: 'mainImage',\n    },\n    prepare(selection) {\n      return {...selection}\n    },\n  },\n})\n"
  },
  {
    "path": "sanity/static/.gitkeep",
    "content": "Files placed here will be served by the Sanity server under the `/static`-prefix\n"
  },
  {
    "path": "sanity/tailwind.config.js",
    "content": "module.exports = {\n  content: [\n'./pages/**/*.{js,ts,jsx,tsx}',\n'./components/**/*.{js,ts,jsx,tsx}',\n'./app/**/*.{js,ts,jsx,tsx}',\n],\n  theme: {\n    extend: {},\n  },\n  plugins: [],\n}"
  },
  {
    "path": "sanity/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2017\",\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"noEmit\": true,\n    \"esModuleInterop\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"node\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"preserve\",\n    \"incremental\": true\n  },\n  \"include\": [\"**/*.ts\", \"**/*.tsx\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "sanity.cli.ts",
    "content": "import { defineCliConfig } from \"@sanity/cli\";\n\nexport default defineCliConfig({\n  api: {\n    projectId: \"5ec749u0\",\n    dataset: \"production\",\n  },\n});\n"
  },
  {
    "path": "styles/globals.css",
    "content": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n@font-face {\n  font-family: \"Barlow\";\n  font-style: normal;\n  font-weight: 400;\n  font-display: swap;\n  src: url(/fonts/Barlow/Barlow-400.woff2) format(\"woff2\");\n}\n@font-face {\n  font-family: \"Barlow\";\n  font-style: normal;\n  font-weight: 500;\n  font-display: swap;\n  src: url(/fonts/Barlow/Barlow-500.woff2) format(\"woff2\");\n}\n@font-face {\n  font-family: \"Barlow\";\n  font-style: normal;\n  font-weight: 600;\n  font-display: swap;\n  src: url(/fonts/Barlow/Barlow-600.woff2) format(\"woff2\");\n}\n@font-face {\n  font-family: \"Barlow\";\n  font-style: normal;\n  font-weight: 700;\n  font-display: swap;\n  src: url(/fonts/Barlow/Barlow-700.woff2) format(\"woff2\");\n}\n@font-face {\n  font-family: \"Barlow\";\n  font-style: normal;\n  font-weight: 800;\n  font-display: swap;\n  src: url(/fonts/Barlow/Barlow-800.woff2) format(\"woff2\");\n}\n@font-face {\n  font-family: \"Inter\";\n  font-style: normal;\n  font-weight: 100 900;\n  font-display: swap;\n  src: url(/fonts/Inter-var.woff2) format(\"woff2\");\n}\n@font-face {\n  font-family: \"Sarina\";\n  font-style: normal;\n  font-weight: normal;\n  font-display: swap;\n  src: url(/fonts/Sarina/Sarina-400.woff2) format(\"woff2\");\n}\n\nbody,\nhtml {\n  overflow-x: hidden;\n  scroll-behavior: auto;\n}\nbody::-webkit-scrollbar {\n  width: 6px;\n}\n\n/* Adding Scroll Margin for top */\n* {\n  scroll-margin-top: 80px;\n}\n@media screen and (max-width: 640px) {\n  * {\n    scroll-margin-top: 60px;\n  }\n  body::-webkit-scrollbar {\n    width: 2px;\n  }\n}\n\npre::-webkit-scrollbar {\n  display: none;\n}\n\nbody.dark {\n  background-color: #181a1b;\n}\n\n::-webkit-scrollbar {\n  width: 6px;\n}\n\n::-webkit-scrollbar-track {\n  background: transparent;\n}\n::-webkit-scrollbar-thumb {\n  background-color: #b3b3b3;\n}\n\n.dark::-webkit-scrollbar-thumb {\n  background-color: #393e41;\n}\n\n.no-scrollbar::-webkit-scrollbar {\n  display: none;\n}\n\n.lock-scroll {\n  overflow: hidden !important;\n}\n\n.horizontal-scrollbar::-webkit-scrollbar {\n  height: 3px;\n  border-redius: 999px;\n}\n\n/* For preventing the blue highlight color box on tap(click) */\n* {\n  -webkit-tap-highlight-color: transparent;\n}\n\n.auto-row {\n  -webkit-margin-before: auto;\n  margin-block-start: auto;\n}\n\n/* Code Line Highlighting START */\ncode {\n  counter-reset: line;\n}\n\ncode span.line {\n  padding: 0px 12px;\n  border-left: 4px solid transparent;\n}\n\nspan.line > span {\n  padding: 2px 0;\n  width: 100%;\n}\n\nspan.line > span:last-child {\n  padding-right: 50px;\n}\ncode > .line::before {\n  counter-increment: line;\n  content: counter(line);\n\n  /* Other styling */\n  display: inline-block;\n  width: 1rem;\n  margin-right: 1rem;\n  text-align: right;\n  color: gray;\n  font-weight: 500;\n  border-right: 4px solid transparent;\n}\n\n.highlighted {\n  background: rgba(200, 200, 255, 0.1);\n  border-left: 4px solid #3777de !important;\n  filter: saturate(1.5);\n}\n/* Code Line Highlighting ENDS */\n\n/* Nprogress bar Custom Styling (force) : STARTS */\n#nprogress .bar {\n  background-color: rgba(0, 89, 255, 0.7) !important;\n  height: 3px !important;\n}\n.dark #nprogress .bar {\n  background: #fff !important;\n}\n#nprogress .peg {\n  box-shadow: none !important;\n}\n/* Nprogress bar Custom Styling (force) : ENDS */\n\n.blogGrid {\n  display: grid;\n  grid-template-columns: 300px 1fr;\n  grid-template-rows: 1fr;\n}\n\n.blog-pre {\n  border-radius: 0;\n}\n\n.blog-pre > code {\n  width: 100%;\n  display: flex;\n  flex-direction: column;\n}\n\n/* Dual-theme switching: show light by default, dark when body.dark is set */\ndiv[data-theme=\"dark\"] {\n  display: none;\n}\ndiv[data-theme=\"light\"] {\n  display: block;\n}\n.dark div[data-theme=\"dark\"] {\n  display: block;\n}\n.dark div[data-theme=\"light\"] {\n  display: none;\n}\n.react-activity-calendar__calendar {\n  width: 100%;\n  height: 100%;\n}\n.react-activity-calendar {\n  max-width: 100% !important;\n}\n\n@keyframes loadingAnimation {\n  0% {\n    left: -100%; /* Move the bar to the left of the container */\n  }\n  100% {\n    left: 100%; /* Move the bar to the right of the container */\n  }\n}\n\n@keyframes pulse-slow {\n  0%,\n  100% {\n    opacity: 0.2;\n    transform: scale(1);\n  }\n  50% {\n    opacity: 0.3;\n    transform: scale(1.05);\n  }\n}\n\n.animate-pulse-slow {\n  animation: pulse-slow 4s ease-in-out infinite;\n}\n\n/* Skills marquee scroll */\n@keyframes marquee {\n  from {\n    transform: translateX(0);\n  }\n  to {\n    transform: translateX(-50%);\n  }\n}\n.animate-marquee {\n  animation: marquee 40s linear infinite;\n  will-change: transform;\n}\n\n/* Hero section diagonal stripe decorator */\n.hero-stripe {\n  background-image: repeating-linear-gradient(\n    -45deg,\n    transparent,\n    transparent 16px,\n    rgba(0, 0, 0, 0.03) 16px,\n    rgba(0, 0, 0, 0.03) 17px\n  );\n}\n.dark .hero-stripe {\n  background-image: repeating-linear-gradient(\n    -45deg,\n    transparent,\n    transparent 16px,\n    rgba(255, 255, 255, 0.03) 16px,\n    rgba(255, 255, 255, 0.03) 17px\n  );\n}\n\n/* Layers Components or the custom class extends with tailwind */\n@layer components {\n  .bottom_nav_icon {\n    @apply mb-[2px] text-2xl cursor-pointer;\n  }\n  .top-nav-link {\n    @apply list-none mx-1 px-3 py-1 border-black dark:border-white transition-all duration-200 hover:rounded-md hover:bg-gray-100 dark:hover:bg-darkSecondary cursor-pointer text-lg font-semibold select-none sm:text-sm md:text-base;\n  }\n  .contact_field {\n    @apply text-sm font-medium text-black dark:text-white w-full px-4 py-2 m-2 rounded-md border-none outline-none shadow-inner shadow-slate-200 dark:shadow-zinc-800 focus:ring-1 focus:ring-purple-500 dark:bg-darkPrimary dark:placeholder-gray-500;\n  }\n  .title_of_page {\n    @apply text-center text-xl font-bold  dark:bg-darkPrimary dark:text-gray-100;\n  }\n  .icon {\n    @apply text-2xl sm:text-3xl m-1 transform duration-200 lg:hover:scale-150 text-zinc-500 hover:text-zinc-800 dark:hover:text-white cursor-pointer;\n  }\n\n  .page_container {\n    @apply p-5 md:px-24 pb-10 dark:bg-darkPrimary dark:text-gray-200 grid gap-6 grid-cols-1 sm:grid-cols-2  lg:grid-cols-3 xl:grid-cols-4 3xl:grid-cols-5;\n  }\n\n  .blog_bottom_icon {\n    @apply text-3xl p-1 bg-gray-100 dark:bg-darkSecondary sm:bg-transparent ring-1 dark:ring-gray-500 ring-gray-300 sm:hover:bg-gray-100 rounded-md cursor-pointer ml-1;\n  }\n  .blog_bottom_button {\n    @apply block sm:hidden py-1 w-full lg:hover:bg-gray-300 cursor-pointer bg-gray-200 rounded-md transform duration-100 active:scale-90 select-none;\n  }\n  .user_reaction {\n    @apply flex font-semibold items-center cursor-pointer w-full justify-center sm:justify-start sm:w-auto space-x-1 text-base;\n  }\n  .project_link {\n    @apply text-center bg-gray-200 p-2 my-1 rounded-full dark:bg-darkSecondary dark:text-white cursor-pointer shadow dark:shadow-gray-500;\n  }\n  .clickable_button {\n    @apply transform duration-100 active:scale-90 lg:hover:scale-105;\n  }\n\n  .home-section-container {\n    @apply flex gap-2 overflow-x-scroll p-5 md:px-24 w-full min-h-[200px] select-none snap-x lg:snap-none;\n  }\n  .home-content-section {\n    @apply relative min-w-[250px] xl:min-w-[300px] break-words shadow shadow-black/20 dark:shadow-white/20 dark:bg-darkSecondary ring-gray-400 rounded-xl p-3 cursor-pointer select-none  lg:hover:scale-105 scale-95 transition bg-white snap-center lg:snap-align-none md:first:ml-24 md:last:mr-24;\n  }\n  .blog-hover-button {\n    @apply flex items-center space-x-2 border-2 border-white dark:border-zinc-600 px-3 py-1 font-semibold w-min text-white dark:text-white hover:bg-white dark:hover:bg-zinc-600 hover:text-black;\n  }\n  .hover-slide-animation {\n    @apply relative overflow-hidden before:absolute before:h-full before:w-40 before:bg-stone-900 dark:before:bg-gray-50 before:opacity-10 dark:before:opacity-5 before:-right-10 before:-z-10 before:rotate-[20deg] before:scale-y-150 before:top-4 hover:before:scale-[7] before:duration-700;\n  }\n  .pageTop {\n    @apply mt-[44px] md:mt-[60px] max-w-4xl 2xl:max-w-5xl 3xl:max-w-7xl relative mx-auto p-4 mb-10 text-neutral-900 dark:text-neutral-200;\n  }\n  .utilities-svg {\n    @apply !pointer-events-none mb-0 w-8 h-8;\n  }\n  .card {\n    @apply bg-white dark:bg-darkSecondary p-5 sm:p-10 flex flex-col sm:flex-row gap-8 items-center max-w-2xl shadow-md rounded-lg mt-[30%] sm:mt-8 transition-all;\n  }\n  .blog-container {\n    @apply !w-full dark:text-neutral-400 my-5 font-medium;\n  }\n  .prose-typography {\n    @apply prose dark:prose-invert sm:prose-lg blog-container prose-pre:bg-white dark:prose-pre:bg-darkSecondary prose-pre:saturate-150 dark:prose-pre:saturate-100 marker:text-black dark:marker:text-white prose-img:mx-auto prose-img:rounded-md prose-headings:text-gray-900 dark:prose-headings:text-white prose-p:text-gray-700 dark:prose-p:text-gray-300 prose-a:text-gray-900 dark:prose-a:text-white prose-strong:text-gray-900 dark:prose-strong:text-white  prose-code:text-black dark:prose-code:text-gray-200 prose-blockquote:border-gray-300 dark:prose-blockquote:border-gray-700 prose-blockquote:text-gray-600 dark:prose-blockquote:text-gray-400;\n  }\n}\n\n@layer base {\n  body {\n    @apply font-inter bg-darkWhite;\n  }\n  button {\n    @apply outline-none;\n  }\n  hr {\n    @apply !mx-auto !w-1/2 h-0.5 !bg-gray-700 dark:!bg-gray-300 border-0 !rounded-full;\n  }\n\n  table {\n    @apply !border-collapse text-left;\n  }\n\n  table thead tr > th,\n  table tbody tr > td {\n    @apply !p-2 border border-gray-400 align-middle;\n  }\n\n  table thead tr > th {\n    @apply text-black dark:text-white;\n  }\n\n  table thead tr {\n    @apply align-text-top;\n  }\n  table th {\n    @apply font-bold;\n  }\n  table a {\n    @apply !text-blue-500 dark:!text-blue-400;\n  }\n\n  strong {\n    @apply !text-black dark:!text-white !font-bold;\n  }\n\n  /* For Blog page to remove the underline  */\n  h2 > a,\n  h3 > a,\n  h4 > a,\n  h5 > a,\n  h6 > a {\n    @apply !text-black dark:!text-white !font-bold !no-underline;\n  }\n}\n\n@layer utilities {\n  /* Hiding the arrows in the input number */\n  input[type=\"number\"]::-webkit-inner-spin-button,\n  input[type=\"number\"]::-webkit-outer-spin-button {\n    -webkit-appearance: none;\n    margin: 0;\n  }\n}\n"
  },
  {
    "path": "tailwind.config.js",
    "content": "module.exports = {\n  content: [\n    \"./pages/**/*.{js,ts,jsx,tsx}\",\n    \"./components/**/*.{js,ts,jsx,tsx}\",\n    \"./layout/*.{js,jsx,ts,tsx}\",\n  ],\n  darkMode: \"class\", // or 'media' or 'class'\n  theme: {\n    fontFamily: {\n      inter: [\"Inter\", \"sans-serif\"],\n      sarina: [\"Sarina\", \"cursive\"],\n      barlow: [\"Barlow\", \"sans-serif\"],\n      mono: [\"monospace\"],\n    },\n    extend: {\n      colors: {\n        darkPrimary: \"#181A1B\",\n        darkSecondary: \"#25282A\",\n        darkWhite: \"#f2f5fa\",\n        \"dark-3\": \"#b8b8b8\",\n      },\n      listStyleType: {\n        square: \"square\",\n        roman: \"upper-roman\",\n      },\n      animation: {\n        wiggle: \"wiggle 1s ease-in-out infinite\",\n        \"photo-spin\": \"photo-spin 2s 1 linear forwards\",\n      },\n      keyframes: {\n        wiggle: {\n          \"0%, 100%\": { transform: \"rotate(-3deg)\" },\n          \"50%\": { transform: \"rotate(3deg)\" },\n        },\n        \"photo-spin\": {\n          \"0%\": { transform: \"rotate(0deg)\" },\n          \"100%\": { transform: \"rotate(360deg)\" },\n        },\n      },\n      screens: {\n        \"3xl\": \"2000px\",\n        xs: \"480px\",\n      },\n    },\n  },\n  plugins: [\n    require(\"@tailwindcss/line-clamp\"),\n    require(\"@tailwindcss/typography\"),\n  ],\n};\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ES5\", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ // \"lib\": [],                                        /* Specify a set of bundled library declaration files that describe the target runtime environment. */\n    \"module\": \"commonjs\", /* Specify what module code is generated. */ // \"rootDir\": \"./\",                                  /* Specify the root folder within your source files. */\n    \"baseUrl\": \".\", /* Specify the base directory to resolve non-relative module names. */\n    \"paths\": {\n      \"@components/*\": [\n        \"components/*\"\n      ],\n      \"@lib/*\": [\n        \"lib/*\"\n      ],\n      \"@utils/*\": [\n        \"utils/*\"\n      ],\n      \"@content/*\": [\n        \"content/*\"\n      ],\n      \"@styles/*\": [\n        \"styles/*\"\n      ],\n      \"@context/*\": [\n        \"context/*\"\n      ],\n      \"@layout/*\": [\n        \"layout/*\"\n      ],\n      \"@hooks/*\": [\n        \"hooks/*\"\n      ]\n    }, /* Specify a set of entries that re-map imports to additional lookup locations. */\n    \"checkJs\": true, /* Enable error reporting in type-checked JavaScript files. */\n    \"esModuleInterop\": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ // \"preserveSymlinks\": true,                         /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */\n    \"forceConsistentCasingInFileNames\": true, /* Ensure that casing is correct in imports. */ /* Type Checking */\n    \"strict\": true, /* Enable all strict type-checking options. */ // \"noImplicitAny\": true,                            /* Enable error reporting for expressions and declarations with an implied 'any' type. */\n    \"noUnusedLocals\": true, /* Enable error reporting when local variables aren't read. */\n    \"noUnusedParameters\": true, /* Raise an error when a function parameter isn't read. */\n    \"skipLibCheck\": true /* Skip type checking all .d.ts files. */,\n    \"lib\": [\n      \"dom\",\n      \"dom.iterable\",\n      \"esnext\"\n    ],\n    \"allowJs\": true,\n    \"noEmit\": true,\n    \"incremental\": true,\n    \"moduleResolution\": \"node\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"preserve\"\n  },\n  \"include\": [\n    \"next-env.d.ts\",\n    \"**/*.ts\",\n    \"**/*.tsx\",\n    \"pages/sitemap.xml.jsx\"\n  ],\n  \"exclude\": [\n    \"node_modules\",\n    \"sanity/**/*\"\n  ]\n}"
  },
  {
    "path": "utils/date.ts",
    "content": "/* Formats a date as a string in the format 'Month day, year'. */\n\nexport const months = [\n  \"Jan\",\n  \"Feb\",\n  \"Mar\",\n  \"Apr\",\n  \"May\",\n  \"Jun\",\n  \"Jul\",\n  \"Aug\",\n  \"Sep\",\n  \"Oct\",\n  \"Nov\",\n  \"Dec\",\n];\nexport function getFormattedDate(date: Date): string {\n  return `${months[date.getMonth()]} ${date.getDate()}, ${date.getFullYear()}`;\n}\n"
  },
  {
    "path": "utils/functions.ts",
    "content": "/**\n * Locks the scroll of the document by adding a 'lock-scroll' class to the html element.\n * The 'lock-scroll' class should be defined in a global stylesheet and contain styles for disabling scrolling.\n */\nexport function lockScroll() {\n  const root = document.getElementsByTagName(\"html\")[0];\n  root.classList.toggle(\"lock-scroll\"); // class is define in the global.css\n}\n\n/**\n * Removes the scroll lock from the document by removing the 'lock-scroll' class from the html element.\n */\nexport function removeScrollLock() {\n  const root = document.getElementsByTagName(\"html\")[0];\n  root.classList.remove(\"lock-scroll\"); // class is define in the global.css\n}\n\n/**\n * Debounces a function by delaying its execution until a certain amount of time has passed\n * since the last time it was invoked. Only the last invocation within the delay period will be executed.\n */\nexport function debounce(fn: Function, time: number = 300): Function {\n  let timeoutId: ReturnType<typeof setTimeout>;\n  return function (this: any, ...args: any[]) {\n    clearTimeout(timeoutId);\n    timeoutId = setTimeout(() => {\n      fn.apply(this, args);\n    }, time);\n  };\n}\n\ninterface QueryParams {\n  [key: string]: string | number;\n}\nexport function generateUrl(baseUrl: string, queryParams: QueryParams): string {\n  const queryString = Object.entries(queryParams)\n    .map(\n      ([key, value]) =>\n        `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`\n    )\n    .join(\"&\");\n  return `${baseUrl}?${queryString}`;\n}\n"
  },
  {
    "path": "utils/utils.ts",
    "content": "export const DEFAULT_IMAGE_URL: string = \"https://imgur.com/5dYYce8.png\";\nexport const AvatarImage: string = \"https://imgur.com/VAXEwKT.png\";\nexport const homeProfileImage: string = \"https://i.imgur.com/fuciSoZ.jpeg\";\n\nexport const navigationRoutes: string[] = [\n  \"home\",\n  \"about\",\n  \"stats\",\n  \"utilities\",\n  \"blogs\",\n  \"books\",\n  \"certificates\",\n  \"projects\",\n  \"snippets\",\n  \"epigraphs\",\n  \"privacy\",\n  \"newsletter\",\n  \"rss\",\n];\n\nexport const snippetsImages: { [key: string]: string } = {\n  css: \"https://imgur.com/ArD8JIg.png\",\n  js: \"https://imgur.com/lFKi8mB.png\",\n  react: \"https://imgur.com/m2jv6MK.png\",\n  ts: \"https://imgur.com/Ux6L5Uh.png\",\n  supabase: \"https://imgur.com/xgNKVQa.png\",\n};\n\nexport const TIME_IN_SECONDS = {\n  MINUTE: 60,\n  FIVE_MINUTES: 60 * 5,\n  TEN_MINUTES: 60 * 10,\n  FIFTEEN_MINUTES: 60 * 15,\n  THIRTY_MINUTES: 60 * 30,\n  ONE_HOUR: 60 * 60,\n  TWO_HOURS: 60 * 60 * 2,\n  THREE_HOURS: 60 * 60 * 3,\n  SIX_HOURS: 60 * 60 * 6,\n  TWELVE_HOURS: 60 * 60 * 12,\n  ONE_DAY: 60 * 60 * 24,\n  ONE_WEEK: 60 * 60 * 24 * 7,\n} as const;\n"
  },
  {
    "path": "vercel.json",
    "content": "{\n  \"$schema\": \"https://openapi.vercel.sh/vercel.json\",\n  \"cleanUrls\": true,\n  \"headers\": [\n    {\n      \"source\": \"/fonts/Barlow/Barlow-400.woff2\",\n      \"headers\": [\n        {\n          \"key\": \"Cache-Control\",\n          \"value\": \"public, max-age=31536000, immutable\"\n        }\n      ]\n    },\n    {\n      \"source\": \"/fonts/Barlow/Barlow-500.woff2\",\n      \"headers\": [\n        {\n          \"key\": \"Cache-Control\",\n          \"value\": \"public, max-age=31536000, immutable\"\n        }\n      ]\n    },\n    {\n      \"source\": \"/fonts/Barlow/Barlow-600.woff2\",\n      \"headers\": [\n        {\n          \"key\": \"Cache-Control\",\n          \"value\": \"public, max-age=31536000, immutable\"\n        }\n      ]\n    },\n    {\n      \"source\": \"/fonts/Barlow/Barlow-700.woff2\",\n      \"headers\": [\n        {\n          \"key\": \"Cache-Control\",\n          \"value\": \"public, max-age=31536000, immutable\"\n        }\n      ]\n    },\n    {\n      \"source\": \"/fonts/Barlow/Barlow-800.woff2\",\n      \"headers\": [\n        {\n          \"key\": \"Cache-Control\",\n          \"value\": \"public, max-age=31536000, immutable\"\n        }\n      ]\n    },\n    {\n      \"source\": \"/fonts/Inter-var.woff2\",\n      \"headers\": [\n        {\n          \"key\": \"Cache-Control\",\n          \"value\": \"public, max-age=31536000, immutable\"\n        }\n      ]\n    },\n    {\n      \"source\": \"/fonts/Sarina/Sarina-400.woff2\",\n      \"headers\": [\n        {\n          \"key\": \"Cache-Control\",\n          \"value\": \"public, max-age=31536000, immutable\"\n        }\n      ]\n    }\n  ],\n  \"redirects\": [\n    {\n      \"source\": \"/home\",\n      \"destination\": \"/\"\n    },\n    {\n      \"source\": \"/rss\",\n      \"destination\": \"/feed.xml\"\n    },\n    {\n      \"source\": \"/sitemap\",\n      \"destination\": \"/sitemap.xml\"\n    },\n    {\n      \"source\": \"/newsletter\",\n      \"destination\": \"https://j471n.substack.com/\"\n    },\n    {\n      \"source\": \"/cv\",\n      \"destination\": \"https://read.cv/j471n\"\n    },\n    {\n      \"source\": \"/social\",\n      \"destination\": \"https://linktr.ee/j471n\"\n    }\n  ]\n}"
  }
]