@font-face {
  font-family: 'Matcha';
  src: url('../assets/Matcha Vibes.otf') format('opentype');
  font-weight: normal;
  font-style: normal;
}
@font-face {
  font-family: 'Super';
  src: url('../assets/Super Joyful.ttf') format('opentype');
  font-weight: normal;
  font-style: normal;
}
/* Google Fonts (Oregano, Roboto, Space Mono, Unbounded, Outfit, Inter)
   are loaded via a single combined <link> tag in each page's <head>
   instead of @import here. @import inside CSS forces the browser to
   discover and fetch each stylesheet sequentially before it can finish
   parsing this file, which delays first render. A <link> in <head> lets
   the browser discover and fetch all of them in parallel with the CSS
   itself. Same fonts, same display=swap, faster paint. */

.font-roboto {
  font-family: "Roboto", sans-serif;
}
.font-oregano {
  font-family: "Oregano", cursive;
}
.font-matcha {
  font-family: "Matcha";
}
.font-super-joy {
  font-family: "Super";
}

/* Layer 2: display / abstract font. Headings, statements, decorative accents only. */
.font-display {
  font-family: "Unbounded", sans-serif;
  letter-spacing: -0.01em;
}

/* Layer 3: monospace font. Metadata, code, tech labels, technical info. */
.font-mono-tech {
  font-family: "Space Mono", ui-monospace, "SFMono-Regular", monospace;
}

.font-outfit {
  font-family: 'Outfit', sans-serif;
}

/*
 * Fluid content container.
 * Old layout used a single fixed max-w-2xl (42rem) on every breakpoint,
 * which is fine up to laptop but leaves large empty margins on desktop
 * and widescreen monitors. This scales the column width with the
 * viewport between two clamped bounds, so mobile stays exactly as
 * compact as before, laptop is unchanged, and desktop/widescreen use
 * more of the available width without turning into a dashboard-wide
 * layout. Replaces `max-w-2xl` on the main content wrapper only.
 */
.content-col {
  width: 100%;
  max-width: clamp(42rem, 38rem + 10vw, 64rem);
  margin-inline: auto;
}

/* Narrower fluid column for prose-heavy cards (About paragraphs, etc.)
   where wide desktop columns would otherwise create hard-to-read lines. */
.prose-col {
  width: 100%;
  max-width: clamp(42rem, 36rem + 6vw, 52rem);
  margin-inline: auto;
}

/*
 * Fluid type scale.
 * Old headings jumped in two fixed steps (mobile size -> md: size).
 * clamp() lets the same heading grow smoothly with viewport width so
 * it never looks cramped at 375px or undersized at 2560px, without
 * needing a third breakpoint. Sizes below map directly onto the
 * elements that used e.g. `text-2xl md:text-4xl` before.
 */
.text-fluid-hero {
  font-size: clamp(1.75rem, 1.35rem + 1.8vw, 2.75rem);
  line-height: 1.08;
}
.text-fluid-display {
  font-size: clamp(1.5rem, 1.2rem + 1.3vw, 2.5rem);
  line-height: 1.12;
}
.text-fluid-display-lg {
  font-size: clamp(1.875rem, 1.4rem + 2vw, 3.25rem);
  line-height: 1.05;
}
.text-fluid-heading {
  font-size: clamp(1.25rem, 1.08rem + 0.75vw, 1.875rem);
  line-height: 1.2;
}
.text-fluid-body {
  font-size: clamp(0.8125rem, 0.77rem + 0.18vw, 1rem);
}
.text-fluid-small {
  font-size: clamp(0.6875rem, 0.66rem + 0.1vw, 0.8125rem);
}

::selection {
  background-color: rgba(22, 50, 74, 0.15);
  color: #0F1620;
}

::-moz-selection {
  background-color: rgba(22, 50, 74, 0.15);
  color: #0F1620;
}

* {
  -webkit-tap-highlight-color: transparent;
}

/*
 * Fallback transition for interactive elements that rely on a Tailwind
 * transition-* utility (transition-colors, transition-opacity, transition-all,
 * transition-transform) but don't already have an explicit `transition` from
 * a component class below. Scoped to interactive selectors instead of `*` so
 * the browser isn't computing transition timing for every element on the
 * page (most of which never animate) on every style recalculation.
 */
a, button, svg, i, [class*="transition-"] {
  transition: background-color 0.25s cubic-bezier(0.4, 0, 0.2, 1),
    border-color 0.25s cubic-bezier(0.4, 0, 0.2, 1),
    color 0.25s cubic-bezier(0.4, 0, 0.2, 1),
    box-shadow 0.25s cubic-bezier(0.4, 0, 0.2, 1),
    opacity 0.25s cubic-bezier(0.4, 0, 0.2, 1),
    transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);
}

:root {
  /*
   * Light professional theme: white + black + deep navy.
   * Variable NAMES are kept identical to the previous dark theme
   * (--bg-black, --surface, --accent, etc.) so the hundreds of
   * var(--token) references throughout this file and in HTML inline
   * styles don't need to change — only what each token RESOLVES TO
   * changes here. --bg-black now holds the light background color
   * despite its name; think of these as semantic slots (primary bg,
   * accent, text-primary...) rather than literal color names.
   */
  --bg-black: #FFFFFF;          /* primary background: white */
  --bg-secondary: #F2F4F8;      /* secondary background: banners, footer, subtle zone shifts */
  --surface: #FFFFFF;           /* card surface: white, differentiated by border/shadow instead of tone */
  --surface-raised: #F6F8FB;    /* raised surface: hover/active state, nested cards */
  --accent: #16324A;            /* deep navy: CTA, active nav, links, badges, icons */
  --accent-soft: rgba(22, 50, 74, 0.5);
  --accent-dim: rgba(22, 50, 74, 0.08);
  --border-line: rgba(15, 30, 50, 0.09);
  --border-line-strong: rgba(15, 30, 50, 0.16);
  --text-primary: #0F1620;      /* near-black, not pure #000 */
  --text-secondary: #4B5568;
  --text-muted: #8792A3;
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-standard: cubic-bezier(0.4, 0, 0.2, 1);

  /* Fluid section rhythm: replaces fixed mb-12/mb-16 utility pairs on
     major sections so vertical spacing grows gently with viewport
     instead of jumping at each breakpoint. */
  --space-section: clamp(2.75rem, 2.2rem + 2.2vw, 4.5rem);
}

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

html {
  scroll-behavior: smooth;
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}

body {
  background-color: var(--bg-black);
  background-image:
    radial-gradient(ellipse 90% 50% at 50% -10%, rgba(22, 50, 74, 0.05), transparent 60%),
    radial-gradient(ellipse 60% 40% at 100% 20%, rgba(22, 50, 74, 0.035), transparent 55%),
    linear-gradient(to bottom, rgba(15, 30, 50, 0.035) 1px, transparent 1px),
    linear-gradient(to right, rgba(15, 30, 50, 0.035) 1px, transparent 1px);
  background-size: auto, auto, 64px 64px, 64px 64px;
  background-position: top center, top right, center, center;
  background-repeat: no-repeat, no-repeat, repeat, repeat;
  background-attachment: fixed, fixed, scroll, scroll;
  font-family: 'Inter', sans-serif;
  color: var(--text-primary);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
  line-height: 1.6;
  padding-top: 70px;
  position: relative;
}

/* Fine technical grid fades out toward the bottom of the viewport so it
   reads as an atmosphere near the top (hero) rather than a repeating
   pattern for the entire page length. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -2;
  pointer-events: none;
  background: radial-gradient(ellipse 80% 60% at 50% 0%, transparent 30%, var(--bg-black) 85%);
}

/* Subtle noise texture over the whole page — barely visible, breaks up
   the flatness of a pure gradient without reading as "grainy". Opacity
   is much lower than the dark-theme version: the same noise pattern
   reads far more visibly on white than on near-black, so it needs a
   fraction of the strength to stay at the same "barely there" level. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0.012;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

@media (min-width: 768px) {
  body { padding-top: 90px; }
}

.fixed-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 1000;
  background: rgba(255, 255, 255, 0.75);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--border-line);
}

.premium-card {
  position: relative;
  background-color: var(--surface);
  border: 1px solid var(--border-line);
  border-radius: 20px;
  box-shadow: 0 1px 2px rgba(15, 30, 50, 0.04),
    0 16px 40px rgba(15, 30, 50, 0.06),
    inset 0 1px 0 rgba(255, 255, 255, 0.5);
  transition: border-color 0.4s var(--ease-standard),
    box-shadow 0.4s var(--ease-standard),
    transform 0.4s var(--ease-standard);
}

.premium-card:hover {
  border-color: var(--border-line-strong);
  box-shadow: 0 1px 2px rgba(15, 30, 50, 0.05),
    0 20px 48px rgba(15, 30, 50, 0.09),
    0 0 0 1px rgba(22, 50, 74, 0.05) inset,
    inset 0 1px 0 rgba(255, 255, 255, 0.6);
}

@media (min-width: 768px) {
  .premium-card { border-radius: 32px; }
}

@media (min-width: 1024px) {
  .premium-card:hover { transform: translateY(-1px); }
}

.banner-mask {
  background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 1) 100%);
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: 70%;
}

.banner-wrapper {
  position: relative;
  width: 100%;
  height: clamp(140px, 110px + 9vw, 300px);
  overflow: hidden;
  background-color: var(--bg-secondary);
}

.banner-img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  opacity: 0.85;
}

.avatar-container {
  margin-top: -45px;
  position: relative;
  z-index: 20;
}

@media (min-width: 640px) {
  .avatar-container { margin-top: -55px; }
}

@media (min-width: 768px) {
  .avatar-container { margin-top: -65px; }
}

.avatar-frame {
  width: 90px;
  height: 90px;
  border: 5px solid var(--surface);
  border-radius: 50%;
  background: var(--surface-raised);
  box-shadow: 0 10px 26px rgba(15, 30, 50, 0.18), 0 0 0 1px rgba(22, 50, 74, 0.08);
  transition: box-shadow 0.4s cubic-bezier(0.4, 0, 0.2, 1);
  object-fit: cover;
  object-position: center;
}

.avatar-container:hover .avatar-frame {
  box-shadow: 0 12px 30px rgba(15, 30, 50, 0.22), 0 0 0 1px rgba(22, 50, 74, 0.22);
}

@media (min-width: 640px) {
  .avatar-frame { width: 110px; height: 110px; border-width: 6px; }
}

@media (min-width: 768px) {
  .avatar-frame { width: 135px; height: 135px; border-width: 8px; }
}

.status-pill {
  position: absolute;
  bottom: 4px;
  right: 4px;
  width: 16px;
  height: 16px;
  background: #4ADE80;
  border: 3px solid var(--surface);
  border-radius: 50%;
}

@media (min-width: 640px) {
  .status-pill { width: 20px; height: 20px; bottom: 6px; right: 6px; }
}

@media (min-width: 768px) {
  .status-pill { width: 26px; height: 26px; border-width: 4px; bottom: 8px; right: 8px; }
}

.profile-content {
  padding: 0 20px 24px 20px;
}

@media (min-width: 640px) {
  .profile-content { padding: 0 28px 32px 28px; }
}

@media (min-width: 768px) {
  .profile-content { padding: 0 40px 40px 40px; }
}

.hover-box {
  background: rgba(15, 30, 50, 0.015);
  border: 1px solid var(--border-line);
  transition: background-color 0.25s cubic-bezier(0.4, 0, 0.2, 1),
    border-color 0.25s cubic-bezier(0.4, 0, 0.2, 1),
    transform 0.25s cubic-bezier(0.4, 0, 0.2, 1),
    box-shadow 0.25s cubic-bezier(0.4, 0, 0.2, 1);
}

.hover-box:hover {
  background: rgba(15, 30, 50, 0.03);
  border-color: rgba(22, 50, 74, 0.28);
  transform: translateY(-2px);
  box-shadow: 0 10px 24px rgba(15, 30, 50, 0.08);
}

.hover-box:active {
  transform: translateY(0);
  box-shadow: 0 3px 10px rgba(15, 30, 50, 0.07);
}

.section-label {
  font-size: 9px;
  font-weight: 800;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.4em;
  margin-bottom: 20px;
  display: block;
  padding-left: 2px;
}

.reveal {
  animation: revealUp 0.8s cubic-bezier(0.16, 1, 0.3, 1);
}

@keyframes revealUp {
  from { opacity: 0; transform: translateY(20px); }
  to { opacity: 1; transform: translateY(0); }
}

/* Scroll-reveal for sections below the fold: JS toggles .is-visible.
   margin-bottom uses the fluid --space-section token so vertical
   rhythm grows gently with viewport instead of the old two-step
   mb-12/md:mb-16 jump — this overrides the Tailwind utility because
   the CDN's stylesheet is inserted before this file in the cascade. */
.scroll-reveal {
  opacity: 0;
  transform: translateY(16px);
  margin-bottom: var(--space-section);
  transition: opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1),
    transform 0.6s cubic-bezier(0.16, 1, 0.3, 1);
}

.scroll-reveal.is-visible {
  opacity: 1;
  transform: translateY(0);
}

@media (prefers-reduced-motion: reduce) {
  .scroll-reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* Project card clickable state */
.project-card {
  cursor: pointer;
}

.project-card:active {
  transform: scale(0.99);
}

.fixed-header.is-scrolled {
  background: rgba(255, 255, 255, 0.92);
  box-shadow: 0 1px 0 rgba(15, 30, 50, 0.06), 0 8px 24px rgba(15, 30, 50, 0.06);
}

/* Buttons: consistent focus/active feedback used across the site */
.btn-feedback {
  transition: transform 0.15s cubic-bezier(0.4, 0, 0.2, 1),
    box-shadow 0.25s cubic-bezier(0.4, 0, 0.2, 1),
    background-color 0.25s cubic-bezier(0.4, 0, 0.2, 1),
    border-color 0.25s cubic-bezier(0.4, 0, 0.2, 1);
}

.btn-feedback:active {
  transform: scale(0.97);
}

/* Desktop primary nav (Home / About / Projects / Lab / Contact) */
.nav-link {
  position: relative;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-muted);
  padding: 6px 2px;
}

.nav-link::after {
  content: "";
  position: absolute;
  left: 0;
  right: 100%;
  bottom: 0;
  height: 1px;
  background: var(--accent);
  transition: right 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}

.nav-link:hover {
  color: var(--text-secondary);
}

.nav-link:hover::after {
  right: 0;
}

.nav-link.is-active {
  color: var(--accent);
}

.nav-link.is-active::after {
  right: 0;
}

/* Inner-page hero label (used on about, projects, lab) */
.page-eyebrow {
  font-size: 9px;
  font-weight: 800;
  letter-spacing: 0.35em;
  text-transform: uppercase;
  color: var(--text-muted);
}

/* Filter chips (used on projects) */
.filter-chip {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 7px 14px;
  border-radius: 999px;
  border: 1px solid var(--border-line);
  background: rgba(15, 30, 50, 0.015);
  color: var(--text-secondary);
  transition: color 0.25s cubic-bezier(0.4, 0, 0.2, 1),
    border-color 0.25s cubic-bezier(0.4, 0, 0.2, 1),
    background-color 0.25s cubic-bezier(0.4, 0, 0.2, 1);
}

.filter-chip:hover {
  color: var(--text-primary);
  border-color: var(--border-line-strong);
}

.filter-chip.is-active {
  color: #FFFFFF;
  background: var(--accent);
  border-color: var(--accent);
}

/* Search field (used on projects) */
.search-field {
  background: rgba(15, 30, 50, 0.015);
  border: 1px solid var(--border-line);
  border-radius: 14px;
  transition: border-color 0.25s cubic-bezier(0.4, 0, 0.2, 1),
    background-color 0.25s cubic-bezier(0.4, 0, 0.2, 1);
}

.search-field:focus-within {
  border-color: rgba(22, 50, 74, 0.35);
  background: rgba(15, 30, 50, 0.03);
}

.search-field input {
  background: transparent;
  color: var(--text-primary);
  outline: none;
  width: 100%;
  border: none;
}

.search-field input::placeholder {
  color: var(--text-muted);
}

/* Project detail modal */
.modal-scrim {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.7);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  z-index: 1200;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s ease;
  overflow-y: auto;
  align-items: flex-start;
  padding: max(4rem, 5vh) 1rem;
}

.modal-scrim.is-open {
  opacity: 1;
  pointer-events: auto;
}

.modal-panel {
  transform: translateY(12px) scale(0.98);
  opacity: 0;
  transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1),
    opacity 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}

.modal-scrim.is-open .modal-panel {
  transform: translateY(0) scale(1);
  opacity: 1;
}

/* Lab experimental surface */
.lab-grid-bg {
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(to right, rgba(15, 30, 50, 0.04) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(15, 30, 50, 0.04) 1px, transparent 1px);
  background-size: 28px 28px;
  mask-image: radial-gradient(ellipse 70% 60% at 50% 0%, #000 40%, transparent 100%);
  pointer-events: none;
}

.lab-noise {
  position: absolute;
  inset: 0;
  opacity: 0.02;
  pointer-events: none;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

.lab-card {
  position: relative;
  background: rgba(15, 30, 50, 0.015);
  border: 1px solid var(--border-line);
  border-radius: 20px;
  overflow: hidden;
  transition: border-color 0.3s cubic-bezier(0.4, 0, 0.2, 1),
    transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}

.lab-card:hover {
  border-color: rgba(22, 50, 74, 0.3);
  transform: translateY(-3px);
}

.lab-card:focus-visible {
  outline: none;
  border-color: rgba(250, 204, 21, 0.4);
}

/* Page transition wrapper */
.page-enter {
  animation: pageEnter 0.5s cubic-bezier(0.16, 1, 0.3, 1);
}

@keyframes pageEnter {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  .page-enter { animation: none; }
}

/* Spotify widget equalizer bars */
@keyframes eqBar {
  0%, 100% { height: 30%; }
  50% { height: 100%; }
}

/* Basic reset for elements that no longer get Next.js/Tailwind defaults */
button {
  font: inherit;
  color: inherit;
  background: none;
  border: none;
  cursor: pointer;
}

a {
  color: inherit;
  text-decoration: none;
}

img {
  display: block;
  max-width: 100%;
}

/* Gradient hairline divider: thin, subtle, used between stacked cards
   instead of a plain solid border when a section wants extra separation. */
.hairline {
  height: 1px;
  background: linear-gradient(to right, transparent, var(--border-line-strong) 50%, transparent);
  border: none;
}

/*
 * Soft ambient lighting accent. Purely decorative, positioned absolutely
 * by the caller. Reduced to a very low opacity, wide blur so it reads
 * as ambient light falling across a surface — not a visible "glowing
 * orb" — matching a professional/editorial visual register rather than
 * a personal-site glow effect. Respects reduced-motion.
 */
.glow-orb {
  position: absolute;
  border-radius: 50%;
  filter: blur(80px);
  pointer-events: none;
  opacity: 0.35;
  animation: orbDrift 18s ease-in-out infinite;
}

@keyframes orbDrift {
  0%, 100% { transform: translate(0, 0); }
  50% { transform: translate(10px, -8px); }
}

@media (prefers-reduced-motion: reduce) {
  .glow-orb { animation: none; }
}

/* Mobile nav menu (slide-down panel triggered by the header toggle). */
.mobile-nav-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 10px;
  color: var(--text-secondary);
  transition: color 0.25s var(--ease-standard), background-color 0.25s var(--ease-standard);
}

.mobile-nav-toggle:hover,
.mobile-nav-toggle[aria-expanded="true"] {
  color: var(--accent);
  background: var(--accent-dim);
}

.mobile-nav-toggle .icon-close,
.mobile-nav-toggle[aria-expanded="true"] .icon-open {
  display: none;
}
.mobile-nav-toggle[aria-expanded="true"] .icon-close {
  display: block;
}

@media (min-width: 768px) {
  .mobile-nav-toggle { display: none; }
}

.mobile-nav-panel {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 999;
  background: rgba(255, 255, 255, 0.97);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  border-bottom: 1px solid var(--border-line);
  padding: 84px 24px 20px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  transform: translateY(-8px);
  opacity: 0;
  pointer-events: none;
  transition: transform 0.3s var(--ease-out), opacity 0.3s var(--ease-out);
}

.mobile-nav-panel.is-open {
  transform: translateY(0);
  opacity: 1;
  pointer-events: auto;
}

@media (min-width: 768px) {
  .mobile-nav-panel { display: none; }
}

.mobile-nav-panel a {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--text-secondary);
  padding: 13px 4px;
  border-bottom: 1px solid var(--border-line);
  transition: color 0.2s var(--ease-standard), padding-left 0.2s var(--ease-standard);
}

.mobile-nav-panel a:last-child { border-bottom: none; }

.mobile-nav-panel a:hover,
.mobile-nav-panel a.is-active {
  color: var(--accent);
  padding-left: 8px;
}

@media (prefers-reduced-motion: reduce) {
  .mobile-nav-panel { transition: opacity 0.15s linear; }
}

/*
 * Tailwind utility fallbacks.
 * These duplicate a handful of classes that the Tailwind Play CDN already
 * generates at runtime. They exist purely as a safety net: if the CDN
 * script fails to load (offline, blocked, or a future version change),
 * font sizes and letter-spacing critical to the layout still render
 * correctly instead of silently falling back to browser defaults.
 */
.text-\[8px\]  { font-size: 8px; }
.text-\[9px\]  { font-size: 9px; }
.text-\[10px\] { font-size: 10px; }
.text-\[11px\] { font-size: 11px; }
.text-\[13px\] { font-size: 13px; }
.text-\[14px\] { font-size: 14px; }
.text-\[16px\] { font-size: 16px; }

.tracking-\[0\.15em\] { letter-spacing: 0.15em; }
.tracking-\[0\.2em\]  { letter-spacing: 0.2em; }
.tracking-\[0\.3em\]  { letter-spacing: 0.3em; }

.bg-\[\#22c55e26\] { background-color: #22c55e26; }
.skew-x-\[-6deg\]  { transform: skewX(-6deg); }

.backdrop-blur-md {
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}

/*
 * ============================================================
 * THEME OVERRIDE: dark bronze/editorial -> light white/black/navy.
 * ============================================================
 * The HTML still uses the original Tailwind color utility class names
 * (text-white, text-gray-400, text-yellow-400, etc.) because those
 * classes are also relied on for non-color behavior (JS toggles them,
 * focus states reference them, and rewriting every class name across
 * 4 HTML files and multiple JS files risks missing one). Rather than
 * edit the markup, this block remaps what those utility classes
 * render as for the new light theme, using !important to guarantee it
 * wins regardless of the Tailwind Play CDN's injection order. This is
 * the single source of truth for "what color is X" — change hex
 * values here, not each individual utility.
 *
 * IMPORTANT DIRECTION CHANGE FROM THE PREVIOUS DARK THEME:
 * text-white was the primary heading/body color on a black background
 * — on a white background it must become near-black instead. The gray
 * scale is also inverted: text-gray-700/800 were used as "barely
 * visible until hover" idle icon states on black (dark gray reads as
 * near-invisible on black) — on white the same idle-state intent
 * requires a LIGHT gray instead. The relative hierarchy (400 = most
 * prominent secondary text -> 800 = most washed out) is preserved;
 * only the direction inverts.
 */

/* Primary heading/body text: was white-on-black, now near-black-on-white */
.text-white { color: var(--text-primary) !important; }

/* Gray scale, inverted for light backgrounds (see comment above).
   Hierarchy preserved: 400 = prominent secondary text ... 800 = idle/
   barely-visible icon state. 900 is intentionally left alone — it's
   only ever used as text-gray-900 on a white button background
   (bg-white text-gray-900), which is already correct for light mode. */
.text-gray-300 { color: #4B5568 !important; }
.text-gray-400 { color: var(--text-secondary) !important; }
.text-gray-500 { color: #5C6678 !important; }
.text-gray-600 { color: var(--text-muted) !important; }
.text-gray-700 { color: #C7CDD6 !important; }
.text-gray-800 { color: #D5DAE1 !important; }

/* border-gray-900 is used as the default card border on several
   secondary cards (Hello Visitors, Development Philosophy, Projects
   preview, decode panel). Aligned to the same border token as
   .premium-card / .hover-box so every card surface reads as one
   consistent system. */
.border-gray-900 { border-color: var(--border-line) !important; }

/* Secondary card surface — same layered-surface treatment as
   .premium-card but for the simpler border+backdrop-blur cards
   (Hello Visitors, Development Philosophy, Projects preview) so the
   whole page reads as one card system. */
.bg-surface { background-color: var(--surface) !important; }

/*
 * yellow-* -> primary accent (deep navy, var(--accent))
 * fuchsia-* -> secondary accent (muted slate-blue, the site's
 *              original two-tone divider/label pattern, now tuned
 *              for a light background)
 */
:is(.text-yellow-300, .text-yellow-400, .text-yellow-500) {
  color: var(--accent) !important;
}
.text-yellow-400\/90 { color: rgba(22, 50, 74, 0.9) !important; }

:is(.bg-yellow-400, .bg-yellow-500) {
  background-color: var(--accent) !important;
}
.bg-yellow-400\/10 { background-color: rgba(22, 50, 74, 0.08) !important; }

.border-yellow-400\/20 { border-color: rgba(22, 50, 74, 0.2) !important; }
.border-yellow-400\/40 { border-color: rgba(22, 50, 74, 0.4) !important; }

.ring-yellow-400\/50 { --tw-ring-color: rgba(22, 50, 74, 0.5) !important; }
.ring-yellow-400\/60 { --tw-ring-color: rgba(22, 50, 74, 0.6) !important; }

.shadow-yellow-400\/20 { --tw-shadow-color: rgba(22, 50, 74, 0.15) !important; }

.accent-yellow-500 { accent-color: var(--accent) !important; }

:is(.text-fuchsia-300, .text-fuchsia-400) {
  color: #3D5A80 !important;
}
:is(.bg-fuchsia-300, .bg-fuchsia-400) {
  background-color: #3D5A80 !important;
}

/* Group-hover variants used on Connect/tech cards */
.group:hover .group-hover\:text-yellow-400 { color: var(--accent) !important; }
.group:focus-visible .group-focus-visible\:text-yellow-400 { color: var(--accent) !important; }
.hover\:text-fuchsia-300:hover { color: #3D5A80 !important; }
.hover\:bg-yellow-400\/10:hover { background-color: rgba(22, 50, 74, 0.08) !important; }
.hover\:border-yellow-400\/40:hover { border-color: rgba(22, 50, 74, 0.4) !important; }
.hover\:border-yellow-400\/20:hover { border-color: rgba(22, 50, 74, 0.2) !important; }
.hover\:text-yellow-400:hover { color: var(--accent) !important; }
.hover\:shadow-yellow-400\/20:hover { --tw-shadow-color: rgba(22, 50, 74, 0.15) !important; }
.focus-visible\:border-yellow-400\/40:focus-visible { border-color: rgba(22, 50, 74, 0.4) !important; }
.focus-visible\:ring-yellow-400\/60:focus-visible { --tw-ring-color: rgba(22, 50, 74, 0.6) !important; }
.focus-visible\:ring-yellow-400\/50:focus-visible { --tw-ring-color: rgba(22, 50, 74, 0.5) !important; }

/* group-hover:text-white (arrow icons that go from idle gray to fully
   visible on hover) — was white-on-black, now near-black-on-white. */
.group:hover .group-hover\:text-white { color: var(--text-primary) !important; }

/* bg-white is used as a small decorative divider line (w-8 h-px)
   before section eyebrows (ABOUT / PROJECTS / LAB). On the dark theme
   a white line on black was the highest-contrast divider; on a white
   background it would be invisible, so it's remapped to the text
   color used for the matching line right next to it. */
.w-8.h-px.bg-white { background-color: var(--text-primary) !important; }

/* text-white/60 (name "rizky dian permana" on About) was a
   60%-opacity white on black for a slightly muted look — on white it
   needs to be a muted dark tone instead of nearly-invisible pale text. */
.text-white\/60 { color: var(--text-secondary) !important; }

/* Circuit card inactive-state dot (bg-gray-700, toggled off when
   bg-yellow-400/accent is toggled on). Dark gray read as "dim/off" on
   a black card; on white it needs to be a pale gray instead so "off"
   still reads as muted rather than as a solid dark dot. */
.bg-gray-700 { background-color: #C7CDD6 !important; }

/* Star rating icon (yellow-300) and modal solid CTA (bg-yellow-400 + black text) */
.text-yellow-300 { color: var(--accent) !important; }
:is(.bg-yellow-400).text-black {
  background-color: var(--accent) !important;
  color: #FFFFFF !important;
}

/*
 * Timeline / decorative tonal variation (previously a small rainbow:
 * cyan, emerald, fuchsia per era; then bronze-tonal). Remapped again
 * to sit correctly on a white background — same relative
 * differentiation between eras, deepened for light-background contrast.
 */
.text-cyan-400 { color: #2C6E8E !important; }              /* cool blue — earliest era */
:is(.text-emerald-400, .text-emerald-300) { color: #3D7A54 !important; } /* deep sage-green — middle era + terminal/decode values */
.text-green-400 { color: #3D7A54 !important; }              /* decorative underline (Sumatra Utara) */
.text-fuchsia-400 { color: #3D5A80 !important; }            /* Instagram icon */
.text-blue-400 { color: #2C6E8E !important; }                /* Telegram icon */

/* text-red-400 covers both decorative emphasis ("kerjaan", "kewajiban")
   and the error state (failed fetch, audio error) — a clear, readable
   red on white rather than the muted red used on the dark theme.
   text-red-500 (HTML5 brand icon) is intentionally left untouched:
   it's a real technology brand color, not a theme color. */
:is(.text-red-400, .text-red-400\/80) { color: #C0392B !important; }

/* Plain text-blue-500/600 are generic repo/code icons (not the CSS3
   brand icon, which is always referenced via the group-hover: variant
   and is left untouched as a real technology brand color). */
:is(.text-blue-500, .text-blue-600) { color: #2C6E8E !important; }

/* text-muted-foreground is a shadcn/ui-style token name that Tailwind's
   Play CDN never generates (it's not a real Tailwind utility), so this
   text was rendering unstyled/inherited before. Fixed to the intended
   muted tone. */
.text-muted-foreground { color: var(--text-muted) !important; }

/* Decode panel "terminal window" (bg-black/40) on the Lab page. At 40%
   opacity over a white card this composited to a washed-out mid-gray
   with poor label contrast — a real regression from the dark theme,
   where the same class produced a genuinely dark window. Replaced with
   a solid dark panel so it still reads as a terminal/code window
   (an intentional dark accent inside an otherwise light page, the way
   code editors keep a dark terminal in a light theme) rather than a
   translucent gray smear. Labels inside it (text-gray-600 ->
   var(--text-muted)) are given a lighter override scoped to this
   panel so they stay legible against the dark backdrop. */
.bg-black\/40 {
  background-color: #12161f !important;
}
.bg-black\/40 .text-gray-600 {
  color: #8792A3 !important;
}
.bg-black\/40 :is(.text-emerald-400, .text-emerald-300) {
  color: #6EE7A8 !important;
}

/* "hindia fans" badge: pale green pill (bg-[#22c55e26]) with the
   surrounding text-muted-foreground italic wrapper. On light mode the
   pastel-green pill needs a matching dark-green text inside it rather
   than neutral gray, so it reads as one cohesive badge. */
.bg-\[\#22c55e26\] { color: #1F7A45 !important; }

/* Music player controls (shuffle/prev/next/like/mute icons) use
   text-slate-400/500, which was a reasonable mid-tone on black but
   fails contrast (2.6:1) against white — not enough for an
   interactive icon button. Darkened to meet the ~3:1 minimum for
   non-text UI components. */
.text-slate-400 { color: #64748B !important; }
.text-slate-500 { color: #64748B !important; }
