/* ==========================================================================
   pablolp.es — single stylesheet.

   Light/dark palette lives entirely in custom properties; dark mode is driven
   by prefers-color-scheme with no JavaScript and no toggle. Every multi-column
   area reflows with auto-fit/minmax or flex-wrap, so the only media queries
   here are the narrow-viewport padding trim and the nav hamburger.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Design tokens
   -------------------------------------------------------------------------- */

:root {
  color-scheme: light dark;

  --bg: #eef1f5;
  --surface: #ffffff;
  --surface-2: #f7f9fb;
  --ink: #101720;
  --body: #4a5460;
  --muted: #7b8694;
  --accent: #0078ff;
  --accent-ink: #ffffff;
  --accent-soft: #e6f0ff;
  --rule: #e1e7ee;
  --deep: #0b1420;
  --deep-ink: #ffffff;
  --deep-body: #9fb0c2;
  --deep-rule: #1e2c3c;
  --tile: #f2f5f8;

  /* Hero badge fill. Literal rather than derived from --accent: the hero band
     is --deep in both schemes, so this reads the same either way. */
  --badge-fill: rgba(0, 120, 255, 0.14);

  /* Project artwork backgrounds, carried over from the previous site. */
  --art-tfm: #024e6f;
  --art-vue: #bff3db;
  --art-telegram: #0a50af;
  --art-alpha: linear-gradient(#0a0d52, #3b2575);

  --shadow-card: 0 1px 1px rgba(0, 0, 0, 0.05), 0 2px 8px rgba(11, 20, 32, 0.09);
  --shadow-project: 0 1px 1px rgba(0, 0, 0, 0.05), 0 12px 24px -14px rgba(11, 20, 32, 0.35);

  --container: 1180px;
  --gutter: 32px;
  --card-pad: 40px;

  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
    "Liberation Mono", monospace;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0a0e13;
    --surface: #141a22;
    --surface-2: #10161d;
    --ink: #eef3f8;
    --body: #a6b2bf;
    --muted: #78848f;
    --accent: #4da3ff;
    --accent-ink: #06121f;
    --accent-soft: #15273a;
    --rule: #222c38;
    --deep: #080c11;
    --deep-ink: #eef3f8;
    --deep-body: #96a4b3;
    --deep-rule: #1d2734;
    --tile: #1b232c;

    --art-vue: #105635;
  }

  /* Zara.svg and sdui.svg are single-colour dark artwork — invert them so they
     stay legible against the dark --tile. Multi-colour logos (logo-poly.svg,
     logo-tfg.svg) must NOT get this class: inverting them wrecks their palette.
     Telegram.svg carries its own white fill and needs no help either. */
  .invert-on-dark {
    filter: invert(100%);
  }
}

/* --------------------------------------------------------------------------
   2. Base
   -------------------------------------------------------------------------- */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
}

body {
  background: var(--bg);
  color: var(--body);
  font-family: var(--font);
  font-size: 16px;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

h1,
h2,
h3 {
  margin: 0;
  color: var(--ink);
  font-weight: 700;
  letter-spacing: -0.02em;
}

p {
  margin: 0;
}

img {
  max-width: 100%;
}

a {
  color: var(--accent);
  text-decoration: none;
}

a:hover {
  color: var(--ink);
}

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

/* Employer and school names linking to LinkedIn, the same treatment the CV
   uses. They sit inside lines that already have a colour (--accent on the
   employer line, --body on the fact detail), so the link keeps that colour and
   earns its affordance from the underline instead — which then strengthens on
   hover. Browsers without color-mix() just get a solid underline. */
.entity-link {
  color: inherit;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 2px;
  text-decoration-color: color-mix(in srgb, currentColor 35%, transparent);
}

.entity-link:hover {
  color: inherit;
  text-decoration-color: currentColor;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: 14px;
  font-weight: 700;
  padding: 12px 18px;
}

.skip-link:focus {
  left: 0;
  color: var(--accent-ink);
}

/* Accessible label for controls whose only visible content is an icon. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Long prose only — headings and short labels are better off ragged. */
.prose {
  text-wrap: pretty;
}

/* Inline emphasis inside localised copy. Body text is --body, so bold earns
   its weight from the darker --ink as much as from the weight itself — 600
   rather than the 700 the headings use, which would shout inside a sentence.
   <em> needs no rule: the browser default italic is already right. */
.prose strong {
  color: var(--ink);
  font-weight: 600;
}

/* Type names and API symbols. The tint keeps it distinct from bold without
   another colour, and 0.9em stops the mono face sitting taller than the
   sentence around it. */
code {
  padding: 0.1em 0.35em;
  border-radius: 4px;
  background: var(--tile);
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: 0.9em;
}

/* --------------------------------------------------------------------------
   3. Layout primitives
   -------------------------------------------------------------------------- */

.container {
  max-width: var(--container);
  margin: 0 auto;
  padding-left: var(--gutter);
  padding-right: var(--gutter);
}

.sections {
  display: flex;
  flex-direction: column;
  gap: 40px;
  padding-top: 48px;
  padding-bottom: 56px;
}

.card {
  background: var(--surface);
  padding: var(--card-pad);
  box-shadow: var(--shadow-card);
}

/* Sticky nav overlaps in-page anchors; give every target room to clear it. */
[id] {
  scroll-margin-top: 84px;
}

/* Section heading with the accent rule under it — the modernised version of
   DevFolio's fixed 100px bar. align-self keeps the rule as wide as the text. */
.section-title {
  font-size: 24px;
  padding-bottom: 12px;
  border-bottom: 3px solid var(--accent);
  align-self: flex-start;
}

.label {
  font-size: 11.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--muted);
}

/* --------------------------------------------------------------------------
   4. Navigation
   -------------------------------------------------------------------------- */

.site-nav {
  position: sticky;
  top: 0;
  z-index: 50;
  background: var(--surface);
  border-bottom: 1px solid var(--rule);
}

.nav-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
  padding-top: 16px;
  padding-bottom: 16px;
}

.brand {
  color: var(--accent);
  font-size: 20px;
  font-weight: 700;
  letter-spacing: -0.02em;
}

.brand:hover {
  color: var(--accent);
}

.nav-menu {
  display: flex;
  align-items: center;
  gap: 26px;
  flex-wrap: wrap;
}

.nav-links {
  display: flex;
  align-items: center;
  gap: 26px;
  flex-wrap: wrap;
}

.nav-links a {
  position: relative;
  color: var(--body);
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  padding-bottom: 4px;
}

.nav-links a:hover,
.nav-links a.is-active {
  color: var(--ink);
}

/* DevFolio's 0 → 35px underline. Kept deliberately: it is part of the site's
   character. The transition is declared under prefers-reduced-motion below. */
.nav-links a::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: 0;
  height: 2px;
  background: var(--accent);
}

.nav-links a:hover::after,
.nav-links a.is-active::after {
  width: 35px;
}

/* Language pills */

.lang-switch {
  display: flex;
  gap: 6px;
  padding-left: 10px;
  border-left: 1px solid var(--rule);
}

.lang-pill {
  display: flex;
  align-items: center;
  gap: 5px;
  font-family: inherit;
  font-size: 12px;
  font-weight: 700;
  color: var(--body);
  background: var(--surface-2);
  border: 0;
  padding: 5px 9px;
  border-radius: 5px;
  cursor: pointer;
}

.lang-pill[aria-pressed="true"] {
  color: var(--accent-ink);
  background: var(--accent);
  cursor: default;
}

.lang-pill img {
  width: 15px;
  height: 11px;
  object-fit: cover;
  border-radius: 2px;
}

/* Hamburger — hidden until the nav collapses. */

.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  /* WebKit's UA stylesheet sets align-items on <button>, which overrides the
     flex default of stretch. Without this the bars below never got a width in
     Safari and the button rendered empty but still tappable. */
  align-items: stretch;
  gap: 5px;
  width: 40px;
  height: 40px;
  padding: 0 8px;
  background: none;
  border: 0;
  cursor: pointer;
}

.nav-toggle span {
  display: block;
  /* Explicit, so the bars do not depend on stretch to get their width. */
  width: 100%;
  height: 2px;
  background: var(--ink);
  border-radius: 2px;
}

.nav-toggle[aria-expanded="true"] span:nth-child(1) {
  transform: translateY(7px) rotate(45deg);
}

.nav-toggle[aria-expanded="true"] span:nth-child(2) {
  opacity: 0;
}

.nav-toggle[aria-expanded="true"] span:nth-child(3) {
  transform: translateY(-7px) rotate(-45deg);
}

/* Progressive enhancement: without JS the menu stays open and the toggle that
   could not operate it stays hidden. The .js class is set in <head>. */
@media (max-width: 767px) {
  .js .nav-toggle {
    display: flex;
  }

  .nav-menu {
    width: 100%;
    flex-direction: column;
    align-items: flex-start;
    gap: 18px;
    padding: 6px 0 10px;
  }

  .js .nav-menu {
    display: none;
  }

  .js .nav-menu.is-open {
    display: flex;
  }

  .nav-links {
    flex-direction: column;
    align-items: flex-start;
    gap: 16px;
  }

  .lang-switch {
    padding-left: 0;
    border-left: 0;
  }
}

/* --------------------------------------------------------------------------
   5. Hero
   -------------------------------------------------------------------------- */

.hero {
  background: var(--deep);
}

.hero-inner {
  display: flex;
  align-items: center;
  gap: 36px;
  flex-wrap: wrap;
  padding-top: 60px;
  padding-bottom: 72px;
}

.hero-portrait {
  width: 132px;
  height: 132px;
  border-radius: 50%;
  object-fit: cover;
  border: 3px solid var(--deep-rule);
  flex: 0 0 auto;
  /* Centres the portrait once the intro wraps below it, by the same trick as
     .hero-cta: alone on its flex line there is free space for the auto margins
     to absorb, beside the intro there is none because .hero-text grows to take
     it first, so they resolve to 0 and the wider layouts are untouched. */
  margin-inline: auto;
}

.hero-text {
  flex: 1 1 420px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.hero-text h1 {
  font-size: 42px;
  line-height: 1.05;
  color: var(--deep-ink);
}

.hero-role {
  font-size: 19px;
  font-weight: 600;
  color: var(--accent);
  margin-top: 8px;
}

.hero-blurb {
  font-size: 16px;
  line-height: 1.55;
  color: var(--deep-body);
  max-width: 640px;
}

/* The hero band is --deep in both schemes, so .prose strong's --ink would be
   near-black on near-black here. Emphasis takes the band's own ink instead. */
.hero-blurb strong {
  color: var(--deep-ink);
}

.badges {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 2px;
  list-style: none;
  margin-block: 0;
  padding: 0;
}

.badges li {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--accent);
  background: var(--badge-fill);
  padding: 5px 11px;
  border-radius: 999px;
}

.hero-cta {
  flex: 0 1 230px;
  min-width: 210px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  /* Centres the column once it wraps below the intro, without a media query
     and without stretching the buttons. Alone on its flex line there is free
     space for the auto margins to absorb; sharing a line with .hero-text there
     is none, because that item grows to consume it first, so they resolve to
     0 and the column stays hard against the intro. */
  margin-inline: auto;
}

/* --------------------------------------------------------------------------
   6. Buttons
   -------------------------------------------------------------------------- */

.btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-size: 14px;
  font-weight: 600;
  padding: 13px 18px;
  border-radius: 6px;
}

.btn-primary {
  background: var(--accent);
  color: var(--accent-ink);
  font-weight: 700;
}

.btn-primary:hover {
  color: var(--accent-ink);
  filter: brightness(1.08);
}

.btn-ghost {
  border: 1px solid var(--deep-rule);
  color: var(--deep-ink);
}

.btn-ghost:hover {
  color: var(--deep-ink);
  border-color: var(--accent);
}

/* Square icon-only buttons, three across the CTA column. */
.icon-row {
  display: flex;
  gap: 8px;
  margin-top: 4px;
}

.icon-btn {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--deep-body);
  border: 1px solid var(--deep-rule);
  border-radius: 6px;
  padding: 9px 0;
}

.icon-btn:hover {
  color: var(--deep-ink);
  border-color: var(--accent);
}

/* The UA `[hidden] { display: none }` rule is namespaced to HTML, so it does
   not apply to the SVG sprite root — hide it explicitly. */
.icon-sprite {
  display: none;
}

.icon {
  width: 1em;
  height: 1em;
  fill: currentColor;
  flex: 0 0 auto;
}

.icon-btn .icon {
  font-size: 17px;
}

/* --------------------------------------------------------------------------
   7. About
   -------------------------------------------------------------------------- */

/* auto-fit/minmax rather than a media query: the two columns collapse to one
   as soon as they cannot both hold 380px.

   The min() is load-bearing. A bare minmax(380px, 1fr) floors the track at
   380px, so once the container is narrower than that the track overflows it
   instead of shrinking — on a 375px phone that pushed the card's text out past
   its own edge and gave the page a horizontal scrollbar. min(380px, 100%) lets
   the floor collapse to the container width. Same reason on every grid below. */
/* One column by default. Deliberately an explicit breakpoint below rather than
   auto-fit: auto-fit split this card at a width that depended on whether a
   scrollbar was present, and the facts were left to stack by coincidence when
   their column happened to fall under their own threshold. Those two widths did
   not line up, so between them the card was two columns while the facts still
   showed 2x2 — an 11px window where the layout flickered as you resized.
   Both decisions now flip together, at a width this file states. */
.about {
  display: grid;
  gap: 48px;
}

@media (min-width: 980px) {
  /* The prose takes the larger share so it runs shorter. The facts stack to
     fill the height beside it — that rule lives with the other .facts
     breakpoints below, which have to stay in ascending order to win. */
  .about {
    grid-template-columns: 1.6fr 1fr;
  }
}

.about-main {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.about-main p {
  font-size: 16.5px;
  line-height: 1.62;
}

.facts {
  display: grid;
  grid-template-columns: 1fr;
  gap: 32px 24px;
  align-content: start;
}

/* There are four facts, so only 1, 2 and 4 columns divide evenly. Three strands
   one of them on a row by itself, which is what auto-fit picked between roughly
   750px and 950px — these breakpoints skip that state. Stated widths rather
   than auto-fit thresholds, which sat a few pixels from flipping and moved
   depending on whether a scrollbar was showing.

   Source order is load-bearing: media queries add no specificity, so these have
   to run in ascending order, and the 980px rule has to come last to win. */
@media (min-width: 480px) {
  .facts {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 870px) {
  .facts {
    grid-template-columns: repeat(4, 1fr);
  }
}

@media (min-width: 980px) {
  /* Beside the prose now, so stack it and let it fill the column height. */
  .facts {
    grid-template-columns: 1fr;
  }
}

.fact {
  display: flex;
  flex-direction: column;
  gap: 9px;
}

.fact-value {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 15px;
  font-weight: 600;
  line-height: 1.4;
  color: var(--ink);
}

.fact-value img {
    width: 17px;
    height: 13px;
    object-fit: cover;
    border-radius: 2px;
    flex: 0 0 auto;
}

.fact p {
  font-size: 14px;
}

.fact-lang {
  display: flex;
  align-items: center;
  gap: 7px;
}

.fact-lang img {
  width: 17px;
  height: 13px;
  object-fit: cover;
  border-radius: 2px;
  flex: 0 0 auto;
}

.fact-lang .level {
  color: var(--muted);
}

.fact-edu {
  line-height: 1.45;
}

.fact-edu strong {
  color: var(--ink);
  font-weight: 600;
}

/* --------------------------------------------------------------------------
   8. Experience
   -------------------------------------------------------------------------- */

.experience {
  display: flex;
  flex-direction: column;
  gap: 26px;
}

.roles {
  display: flex;
  flex-direction: column;
  gap: 22px;
}

/* Deliberately flex, not grid: a fixed `200px 1fr` track cannot collapse and
   squeezes the description to ~90px on a phone. flex-wrap lets the date stack
   above the role around 570px with no media query. */
.role {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 28px;
}

.role:not(:last-child) {
  padding-bottom: 22px;
  border-bottom: 1px solid var(--rule);
}

.role-when {
  flex: 0 0 200px;
  font-size: 13.5px;
  font-weight: 600;
  line-height: 1.5;
  color: var(--muted);
}

.role-where {
  color: var(--body);
  font-weight: 400;
}

.role-body {
  flex: 1 1 340px;
  display: flex;
  flex-direction: column;
  gap: 7px;
}

.role-body h3 {
  font-size: 17.5px;
}

.role-employer {
  font-size: 14.5px;
  font-weight: 600;
  color: var(--accent);
}

.role-desc {
  font-size: 15px;
  line-height: 1.6;
}

/* Detail under a .role-desc, so it matches that type rather than setting its
   own. `outside` markers need the padding-left to keep the discs inside the
   card's content box; there is no global list reset, so the browser default
   margin has to go explicitly. */
.role-points {
  margin: 0;
  padding-left: 19px;
  list-style: disc outside;
  font-size: 15px;
  line-height: 1.6;
}

.role-points li:not(:last-child) {
  margin-bottom: 5px;
}

.role-points li::marker {
  color: var(--accent);
}

/* --------------------------------------------------------------------------
   9. Selected work
   -------------------------------------------------------------------------- */

.work {
  display: flex;
  flex-direction: column;
  gap: 24px;
}

.work-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 20px;
  flex-wrap: wrap;
}

/* No accent rule under this one: "Selected work" is not a card section, so it
   sits on a baseline row with its caption instead. */
.work-head h2 {
  font-size: 24px;
}

.work-head p {
  font-size: 14px;
  color: var(--muted);
}

.tier-label {
  font-size: 11.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.11em;
  color: var(--accent);
}

.tier-label.is-muted {
  color: var(--muted);
}

.work > .tier-label:not(:first-of-type) {
  margin-top: 8px;
}

.tier-pro {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr));
  gap: 30px;
}

.tier-personal {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
  gap: 30px;
}

.tier-student {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr));
  gap: 20px;
}

/* Square corners: DevFolio's .work-box has no radius and that reads as
   deliberate against the rounded buttons and badges. */
.project {
  display: block;
  background: var(--surface);
  box-shadow: var(--shadow-project);
  color: inherit;
}

.project:hover {
  color: inherit;
}

.project-media {
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  height: 170px;
  background: var(--tile);
}

.tier-personal .project-media {
  height: 150px;
}

.project-body {
  padding: 22px 24px 24px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.project-body h3 {
  font-size: 18px;
}

.project-body p {
  font-size: 14.5px;
  line-height: 1.55;
  color: var(--body);
}

.project-body .project-meta {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--accent);
}

.tier-personal .project-body {
  padding: 20px 22px 22px;
  gap: 7px;
}

.tier-personal .project-body h3 {
  font-size: 16.5px;
}

.tier-personal .project-body p {
  font-size: 14px;
}

/* Artwork variants */

.media-zara img {
  height: 120px;
  width: auto;
}

.media-sdui img {
  height: 100px;
  width: auto;
}

.media-tfm {
  background: var(--art-tfm);
}

.media-tfm img {
  height: 100%;
  width: auto;
  object-fit: contain;
}

.media-tfg {
  background: var(--art-vue);
}

.media-tfg img {
  height: 90px;
  width: auto;
}

.media-poly {
  background: var(--art-vue);
}

.media-poly img {
  height: 90px;
  width: auto;
}

/* Tier 3 — compact tiles, border instead of shadow so they sit visibly below
   the two tiers above. */
.tile {
  display: flex;
  align-items: center;
  gap: 14px;
  background: var(--surface);
  border: 1px solid var(--rule);
  padding: 16px 18px;
  color: inherit;
}

.tile:hover {
  color: inherit;
  border-color: var(--accent);
}

.tile-thumb {
  width: 44px;
  height: 44px;
  border-radius: 9px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  overflow: hidden;
}

.tile-thumb img {
  width: 32px;
  height: 32px;
  object-fit: contain;
}

.thumb-alpha {
  background: var(--art-alpha);
}

.thumb-yummy {
  background: url("../img/Yummy-back.png") center / cover;
}

.thumb-yummy img {
  width: 30px;
  height: 30px;
}

.thumb-bot {
  background: var(--art-telegram);
}

.tile-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.tile-title {
  font-size: 14.5px;
  font-weight: 600;
  color: var(--ink);
}

.tile-meta {
  font-size: 12.5px;
  color: var(--muted);
}

/* --------------------------------------------------------------------------
   10. Footer
   -------------------------------------------------------------------------- */

.site-footer {
  background: var(--deep);
}

.footer-inner {
  padding-top: 56px;
  padding-bottom: 28px;
}

.footer-top {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 40px;
  flex-wrap: wrap;
  padding-bottom: 32px;
  border-bottom: 1px solid var(--deep-rule);
}

.footer-intro {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.footer-intro h2 {
  font-size: 30px;
  color: var(--deep-ink);
}

.footer-intro p {
  font-size: 16px;
  line-height: 1.55;
  color: var(--deep-body);
  max-width: 440px;
}

.footer-actions {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
}

.footer-actions .btn {
  font-size: 14.5px;
  gap: 9px;
  padding: 13px 20px;
}

.copyright {
  font-size: 13px;
  color: var(--muted);
  text-align: center;
  padding-top: 22px;
  position: relative;
}

.easter-egg {
  display: none;
  align-items: center;
  justify-content: center;
  position: relative;
  width: 200px;
  height: 200px;
  margin: 10px auto 0;
  pointer-events: none;
}

.easter-egg.started-timer {
  display: flex;
}

.easter-egg.is-revealed {
  pointer-events: auto;
}

.easter-egg-countdown {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 84px;
  height: 84px;
  font-size: 28px;
  font-weight: 700;
  color: var(--muted);
  opacity: 0;
  transition: opacity 0.3s ease;
}

.easter-egg-countdown::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 3px solid var(--rule);
  border-top-color: var(--accent);
}

.easter-egg.started-timer .easter-egg-countdown {
  opacity: 1;
}

.easter-egg.is-revealed .easter-egg-countdown {
  opacity: 0;
}

#easter-egg-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.8s ease;
}

.easter-egg.is-revealed #easter-egg-img {
  opacity: 1;
  visibility: visible;
}

/* --------------------------------------------------------------------------
   11. Narrow viewports
   -------------------------------------------------------------------------- */

/* The layout reflows on its own; this only stops 32px gutters plus 40px card
   padding from eating half the width of a 375px screen. */
@media (max-width: 560px) {
  :root {
    --gutter: 20px;
    --card-pad: 24px;
  }

  .hero-text h1 {
    font-size: 34px;
  }

  .hero-role {
    font-size: 17px;
  }

  .footer-intro h2 {
    font-size: 26px;
  }

  .hero-cta,
  .footer-actions,
  .footer-actions .btn {
    flex: 1 1 100%;
  }
}

/* --------------------------------------------------------------------------
   12. Motion
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }

  .nav-links a::after {
    transition: width 500ms ease;
  }

  .nav-toggle span {
    transition: transform 250ms ease, opacity 250ms ease;
  }

  /* DevFolio's slow zoom on project artwork — one of the nicer details of the
     previous site, kept as-is. */
  .project-media img {
    transition: transform 1s;
  }

  .project:hover .project-media img {
    transform: scale(1.3);
  }

  .btn,
  .icon-btn,
  .tile,
  .lang-pill {
    transition: color 250ms ease, border-color 250ms ease, filter 250ms ease;
  }

  .easter-egg-countdown::before {
    animation: easter-egg-spin 1s linear infinite;
  }
}

@keyframes easter-egg-spin {
  to {
    transform: rotate(360deg);
  }
}
