/*! tailwindcss v4.3.2 | MIT License | https://tailwindcss.com */
@layer properties {
  @supports (((-webkit-hyphens: none)) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color: rgb(from red r g b)))) {
    *, :before, :after, ::backdrop {
      --tw-rotate-x: initial;
      --tw-rotate-y: initial;
      --tw-rotate-z: initial;
      --tw-skew-x: initial;
      --tw-skew-y: initial;
      --tw-border-style: solid;
      --tw-blur: initial;
      --tw-brightness: initial;
      --tw-contrast: initial;
      --tw-grayscale: initial;
      --tw-hue-rotate: initial;
      --tw-invert: initial;
      --tw-opacity: initial;
      --tw-saturate: initial;
      --tw-sepia: initial;
      --tw-drop-shadow: initial;
      --tw-drop-shadow-color: initial;
      --tw-drop-shadow-alpha: 100%;
      --tw-drop-shadow-size: initial;
      --tw-backdrop-blur: initial;
      --tw-backdrop-brightness: initial;
      --tw-backdrop-contrast: initial;
      --tw-backdrop-grayscale: initial;
      --tw-backdrop-hue-rotate: initial;
      --tw-backdrop-invert: initial;
      --tw-backdrop-opacity: initial;
      --tw-backdrop-saturate: initial;
      --tw-backdrop-sepia: initial;
      --tw-ease: initial;
    }
  }
}

@layer theme {
  :root, :host {
    --font-sans: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji",
      "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
    --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
      "Courier New", monospace;
    --ease-out: cubic-bezier(0, 0, .2, 1);
    --default-transition-duration: .15s;
    --default-transition-timing-function: cubic-bezier(.4, 0, .2, 1);
    --default-font-family: var(--font-sans);
    --default-mono-font-family: var(--font-mono);
    --color-darkpink: var(--darkpink);
    --color-white-90: var(--white-90);
    --color-white-70: var(--white-70);
    --font-encode: "Encode Sans SC", sans-serif;
    --font-plex: "IBM Plex Mono", monospace;
    --font-source: "Source Code Pro", monospace;
    --font-inter: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  }
}

@layer base {
  *, :after, :before, ::backdrop {
    box-sizing: border-box;
    border: 0 solid;
    margin: 0;
    padding: 0;
  }

  ::file-selector-button {
    box-sizing: border-box;
    border: 0 solid;
    margin: 0;
    padding: 0;
  }

  html, :host {
    -webkit-text-size-adjust: 100%;
    tab-size: 4;
    line-height: 1.5;
    font-family: var(--default-font-family, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");
    font-feature-settings: var(--default-font-feature-settings, normal);
    font-variation-settings: var(--default-font-variation-settings, normal);
    -webkit-tap-highlight-color: transparent;
  }

  hr {
    height: 0;
    color: inherit;
    border-top-width: 1px;
  }

  abbr:where([title]) {
    -webkit-text-decoration: underline dotted;
    text-decoration: underline dotted;
  }

  h1, h2, h3, h4, h5, h6 {
    font-size: inherit;
    font-weight: inherit;
  }

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

  b, strong {
    font-weight: bolder;
  }

  code, kbd, samp, pre {
    font-family: var(--default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);
    font-feature-settings: var(--default-mono-font-feature-settings, normal);
    font-variation-settings: var(--default-mono-font-variation-settings, normal);
    font-size: 1em;
  }

  small {
    font-size: 80%;
  }

  sub, sup {
    vertical-align: baseline;
    font-size: 75%;
    line-height: 0;
    position: relative;
  }

  sub {
    bottom: -.25em;
  }

  sup {
    top: -.5em;
  }

  table {
    text-indent: 0;
    border-color: inherit;
    border-collapse: collapse;
  }

  :-moz-focusring {
    outline: auto;
  }

  progress {
    vertical-align: baseline;
  }

  summary {
    display: list-item;
  }

  ol, ul, menu {
    list-style: none;
  }

  img, svg, video, canvas, audio, iframe, embed, object {
    vertical-align: middle;
    display: block;
  }

  img, video {
    max-width: 100%;
    height: auto;
  }

  button, input, select, optgroup, textarea {
    font: inherit;
    font-feature-settings: inherit;
    font-variation-settings: inherit;
    letter-spacing: inherit;
    color: inherit;
    opacity: 1;
    background-color: #0000;
    border-radius: 0;
  }

  ::file-selector-button {
    font: inherit;
    font-feature-settings: inherit;
    font-variation-settings: inherit;
    letter-spacing: inherit;
    color: inherit;
    opacity: 1;
    background-color: #0000;
    border-radius: 0;
  }

  :where(select:is([multiple], [size])) optgroup {
    font-weight: bolder;
  }

  :where(select:is([multiple], [size])) optgroup option {
    padding-inline-start: 20px;
  }

  ::file-selector-button {
    margin-inline-end: 4px;
  }

  ::placeholder {
    opacity: 1;
  }

  @supports (not ((-webkit-appearance: -apple-pay-button))) or (contain-intrinsic-size: 1px) {
    ::placeholder {
      color: currentColor;
    }

    @supports (color: color-mix(in lab, red, red)) {
      ::placeholder {
        color: color-mix(in oklab, currentcolor 50%, transparent);
      }
    }
  }

  textarea {
    resize: vertical;
  }

  ::-webkit-search-decoration {
    -webkit-appearance: none;
  }

  ::-webkit-date-and-time-value {
    min-height: 1lh;
    text-align: inherit;
  }

  ::-webkit-datetime-edit {
    display: inline-flex;
  }

  ::-webkit-datetime-edit-fields-wrapper {
    padding: 0;
  }

  ::-webkit-datetime-edit {
    padding-block: 0;
  }

  ::-webkit-datetime-edit-year-field {
    padding-block: 0;
  }

  ::-webkit-datetime-edit-month-field {
    padding-block: 0;
  }

  ::-webkit-datetime-edit-day-field {
    padding-block: 0;
  }

  ::-webkit-datetime-edit-hour-field {
    padding-block: 0;
  }

  ::-webkit-datetime-edit-minute-field {
    padding-block: 0;
  }

  ::-webkit-datetime-edit-second-field {
    padding-block: 0;
  }

  ::-webkit-datetime-edit-millisecond-field {
    padding-block: 0;
  }

  ::-webkit-datetime-edit-meridiem-field {
    padding-block: 0;
  }

  ::-webkit-calendar-picker-indicator {
    line-height: 1;
  }

  :-moz-ui-invalid {
    box-shadow: none;
  }

  button, input:where([type="button"], [type="reset"], [type="submit"]) {
    appearance: button;
  }

  ::file-selector-button {
    appearance: button;
  }

  ::-webkit-inner-spin-button {
    height: auto;
  }

  ::-webkit-outer-spin-button {
    height: auto;
  }

  [hidden]:where(:not([hidden="until-found"])) {
    display: none !important;
  }

  @font-face {
    font-family: Encode Sans SC;
    font-weight: 300;
    font-style: normal;
    src: url("/fonts/EncodeSansSC/EncodeSansSC-Light.woff2") format("woff2"), url("/fonts/EncodeSansSC/EncodeSansSC-Light.ttf") format("truetype");
    font-display: swap;
  }

  @font-face {
    font-family: Encode Sans SC;
    font-weight: 400;
    font-style: normal;
    src: url("/fonts/EncodeSansSC/EncodeSansSC-Regular.woff2") format("woff2"), url("/fonts/EncodeSansSC/EncodeSansSC-Regular.ttf") format("truetype");
    font-display: swap;
  }

  @font-face {
    font-family: Encode Sans SC;
    font-weight: 600;
    font-style: normal;
    src: url("/fonts/EncodeSansSC/EncodeSansSC-SemiBold.woff2") format("woff2"), url("/fonts/EncodeSansSC/EncodeSansSC-SemiBold.ttf") format("truetype");
    font-display: swap;
  }

  @font-face {
    font-family: Encode Sans SC;
    font-weight: 700;
    font-style: normal;
    src: url("/fonts/EncodeSansSC/EncodeSansSC-Bold.woff2") format("woff2"), url("/fonts/EncodeSansSC/EncodeSansSC-Bold.ttf") format("truetype");
    font-display: swap;
  }

  @font-face {
    font-family: IBM Plex Mono;
    font-weight: 100;
    font-style: normal;
    src: url("/fonts/IBMPlexMono/IBMPlexMono-Thin.woff2") format("woff2"), url("/fonts/IBMPlexMono/IBMPlexMono-Thin.ttf") format("truetype");
    font-display: swap;
  }

  @font-face {
    font-family: IBM Plex Mono;
    font-weight: 300;
    font-style: normal;
    src: url("/fonts/IBMPlexMono/IBMPlexMono-Light.woff2") format("woff2"), url("/fonts/IBMPlexMono/IBMPlexMono-Light.ttf") format("truetype");
    font-display: swap;
  }

  @font-face {
    font-family: IBM Plex Mono;
    font-weight: 400;
    font-style: normal;
    src: url("/fonts/IBMPlexMono/IBMPlexMono-Regular.woff2") format("woff2"), url("/fonts/IBMPlexMono/IBMPlexMono-Regular.ttf") format("truetype");
    font-display: swap;
  }

  @font-face {
    font-family: IBM Plex Mono;
    font-weight: 500;
    font-style: normal;
    src: url("/fonts/IBMPlexMono/IBMPlexMono-Medium.woff2") format("woff2"), url("/fonts/IBMPlexMono/IBMPlexMono-Medium.ttf") format("truetype");
    font-display: swap;
  }

  @font-face {
    font-family: IBM Plex Mono;
    font-weight: 600;
    font-style: normal;
    src: url("/fonts/IBMPlexMono/IBMPlexMono-SemiBold.woff2") format("woff2"), url("/fonts/IBMPlexMono/IBMPlexMono-SemiBold.ttf") format("truetype");
    font-display: swap;
  }

  @font-face {
    font-family: IBM Plex Mono;
    font-weight: 700;
    font-style: normal;
    src: url("/fonts/IBMPlexMono/IBMPlexMono-Bold.woff2") format("woff2"), url("/fonts/IBMPlexMono/IBMPlexMono-Bold.ttf") format("truetype");
    font-display: swap;
  }

  @font-face {
    font-family: IBM Plex Mono;
    font-weight: 400;
    font-style: italic;
    src: url("/fonts/IBMPlexMono/IBMPlexMono-Italic.woff2") format("woff2"), url("/fonts/IBMPlexMono/IBMPlexMono-Italic.ttf") format("truetype");
    font-display: swap;
  }

  @font-face {
    font-family: IBM Plex Mono;
    font-weight: 700;
    font-style: italic;
    src: url("/fonts/IBMPlexMono/IBMPlexMono-BoldItalic.woff2") format("woff2"), url("/fonts/IBMPlexMono/IBMPlexMono-BoldItalic.ttf") format("truetype");
    font-display: swap;
  }

  @font-face {
    font-family: Source Code Pro;
    font-weight: 300;
    font-style: normal;
    src: url("/fonts/SourceCodePro/SourceCodePro-Light.woff2") format("woff2"), url("/fonts/SourceCodePro/SourceCodePro-Light.ttf") format("truetype");
    font-display: swap;
  }

  @font-face {
    font-family: Source Code Pro;
    font-weight: 400;
    font-style: normal;
    src: url("/fonts/SourceCodePro/SourceCodePro-Regular.woff2") format("woff2"), url("/fonts/SourceCodePro/SourceCodePro-Regular.ttf") format("truetype");
    font-display: swap;
  }

  @font-face {
    font-family: Source Code Pro;
    font-weight: 500;
    font-style: normal;
    src: url("/fonts/SourceCodePro/SourceCodePro-Medium.woff2") format("woff2"), url("/fonts/SourceCodePro/SourceCodePro-Medium.ttf") format("truetype");
    font-display: swap;
  }

  @font-face {
    font-family: Source Code Pro;
    font-weight: 600;
    font-style: normal;
    src: url("/fonts/SourceCodePro/SourceCodePro-SemiBold.woff2") format("woff2"), url("/fonts/SourceCodePro/SourceCodePro-SemiBold.ttf") format("truetype");
    font-display: swap;
  }

  @font-face {
    font-family: Source Code Pro;
    font-weight: 700;
    font-style: normal;
    src: url("/fonts/SourceCodePro/SourceCodePro-Bold.woff2") format("woff2"), url("/fonts/SourceCodePro/SourceCodePro-Bold.ttf") format("truetype");
    font-display: swap;
  }

  @font-face {
    font-family: Source Code Pro;
    font-weight: 400;
    font-style: italic;
    src: url("/fonts/SourceCodePro/SourceCodePro-Italic.woff2") format("woff2"), url("/fonts/SourceCodePro/SourceCodePro-Italic.ttf") format("truetype");
    font-display: swap;
  }

  @font-face {
    font-family: Source Code Pro;
    font-weight: 700;
    font-style: italic;
    src: url("/fonts/SourceCodePro/SourceCodePro-BoldItalic.woff2") format("woff2"), url("/fonts/SourceCodePro/SourceCodePro-BoldItalic.ttf") format("truetype");
    font-display: swap;
  }

  html {
    scroll-behavior: smooth;
    overflow: hidden scroll;
  }

  html.is-loading body {
    width: 100%;
    position: fixed;
    inset: 0;
    overflow: hidden;
  }

  html.scroll-locked body {
    position: fixed;
    left: 0;
    right: 0;
    overflow: hidden;
  }

  body {
    background-color: var(--background);
    color: var(--foreground);
    font-family: var(--font-source);
    scrollbar-width: thin;
    scrollbar-color: var(--lightblue) transparent;
    overflow-x: hidden;
  }

  ::-webkit-scrollbar {
    width: 8px;
    height: 8px;
  }

  ::-webkit-scrollbar-track {
    background-color: var(--lightblue);
  }

  @supports (color: color-mix(in lab, red, red)) {
    ::-webkit-scrollbar-track {
      background-color: color-mix(in srgb, var(--lightblue) 15%, transparent);
    }
  }

  ::-webkit-scrollbar-track {
    border-radius: 10px;
  }

  ::-webkit-scrollbar-thumb {
    background-color: var(--lightblue);
  }

  @supports (color: color-mix(in lab, red, red)) {
    ::-webkit-scrollbar-thumb {
      background-color: color-mix(in srgb, var(--lightblue) 65%, var(--background) 35%);
    }
  }

  ::-webkit-scrollbar-thumb {
    border: 1px solid var(--lightblue);
    border-radius: 10px;
  }

  ::-webkit-scrollbar-thumb:hover {
    background-color: var(--lightblue);
  }
}

@layer components {
  .bg-az-grid {
    background-color: var(--az-grid-bg);
    background-image: linear-gradient(var(--az-grid-line-bg) 1px, transparent 1px), linear-gradient(90deg, var(--az-grid-line-bg) 1px, transparent 1px);
    background-size: 15px 15px;
  }

  .text-title {
    font-family: var(--font-encode);
    color: var(--color-darkpink);
    font-size: 1.125rem;
    font-weight: 700;
    line-height: 1.25;
  }

  .text-subtitle {
    font-family: var(--font-encode);
    color: var(--color-white-90);
    font-size: 1rem;
    font-weight: 600;
    line-height: 1.35;
  }

  .text-body {
    font-family: var(--font-inter);
    color: var(--color-white-90);
    font-size: .875rem;
    font-weight: 400;
    line-height: 1.6;
  }

  .text-muted {
    font-family: var(--font-inter);
    color: var(--color-white-70);
    font-size: .8125rem;
    font-weight: 400;
    line-height: 1.6;
  }

  .text-emphasis {
    font-family: var(--font-source);
    color: var(--color-white-70);
    font-size: .75rem;
    font-style: italic;
    line-height: 1.5;
  }
}

@layer utilities {
  .visible {
    visibility: visible;
  }

  .fixed {
    position: fixed;
  }

  .static {
    position: static;
  }

  .inline {
    display: inline;
  }

  .min-h-screen {
    min-height: 100vh;
  }

  .w-full {
    width: 100%;
  }

  .transform {
    transform: var(--tw-rotate-x, ) var(--tw-rotate-y, ) var(--tw-rotate-z, ) var(--tw-skew-x, ) var(--tw-skew-y, );
  }

  .resize {
    resize: both;
  }

  .border {
    border-style: var(--tw-border-style);
    border-width: 1px;
  }

  .filter {
    filter: var(--tw-blur, ) var(--tw-brightness, ) var(--tw-contrast, ) var(--tw-grayscale, ) var(--tw-hue-rotate, ) var(--tw-invert, ) var(--tw-saturate, ) var(--tw-sepia, ) var(--tw-drop-shadow, );
  }

  .backdrop-filter {
    -webkit-backdrop-filter: var(--tw-backdrop-blur, ) var(--tw-backdrop-brightness, ) var(--tw-backdrop-contrast, ) var(--tw-backdrop-grayscale, ) var(--tw-backdrop-hue-rotate, ) var(--tw-backdrop-invert, ) var(--tw-backdrop-opacity, ) var(--tw-backdrop-saturate, ) var(--tw-backdrop-sepia, );
    backdrop-filter: var(--tw-backdrop-blur, ) var(--tw-backdrop-brightness, ) var(--tw-backdrop-contrast, ) var(--tw-backdrop-grayscale, ) var(--tw-backdrop-hue-rotate, ) var(--tw-backdrop-invert, ) var(--tw-backdrop-opacity, ) var(--tw-backdrop-saturate, ) var(--tw-backdrop-sepia, );
  }

  .transition {
    transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, content-visibility, overlay, pointer-events;
    transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
    transition-duration: var(--tw-duration, var(--default-transition-duration));
  }

  .ease-out {
    --tw-ease: var(--ease-out);
    transition-timing-function: var(--ease-out);
  }
}

@font-face {
  font-family: Inter;
  font-style: normal;
  font-display: swap;
  font-weight: 400;
  src: url("/_astro/inter-cyrillic-ext-400-normal.BQZuk6qB.woff2") format("woff2"), url("/_astro/inter-cyrillic-ext-400-normal.DQukG94-.woff") format("woff");
  unicode-range: U+460-52F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}

@font-face {
  font-family: Inter;
  font-style: normal;
  font-display: swap;
  font-weight: 400;
  src: url("/_astro/inter-cyrillic-400-normal.obahsSVq.woff2") format("woff2"), url("/_astro/inter-cyrillic-400-normal.HOLc17fK.woff") format("woff");
  unicode-range: U+301, U+400-45F, U+490-491, U+4B0-4B1, U+2116;
}

@font-face {
  font-family: Inter;
  font-style: normal;
  font-display: swap;
  font-weight: 400;
  src: url("/_astro/inter-greek-ext-400-normal.DGGRlc-M.woff2") format("woff2"), url("/_astro/inter-greek-ext-400-normal.KugGGMne.woff") format("woff");
  unicode-range: U+1F??;
}

@font-face {
  font-family: Inter;
  font-style: normal;
  font-display: swap;
  font-weight: 400;
  src: url("/_astro/inter-greek-400-normal.B4URO6DV.woff2") format("woff2"), url("/_astro/inter-greek-400-normal.q2sYcFCs.woff") format("woff");
  unicode-range: U+370-377, U+37A-37F, U+384-38A, U+38C, U+38E-3A1, U+3A3-3FF;
}

@font-face {
  font-family: Inter;
  font-style: normal;
  font-display: swap;
  font-weight: 400;
  src: url("/_astro/inter-vietnamese-400-normal.DMkecbls.woff2") format("woff2"), url("/_astro/inter-vietnamese-400-normal.Bbgyi5SW.woff") format("woff");
  unicode-range: U+102-103, U+110-111, U+128-129, U+168-169, U+1A0-1A1, U+1AF-1B0, U+300-301, U+303-304, U+308-309, U+323, U+329, U+1EA0-1EF9, U+20AB;
}

@font-face {
  font-family: Inter;
  font-style: normal;
  font-display: swap;
  font-weight: 400;
  src: url("/_astro/inter-latin-ext-400-normal.C1nco2VV.woff2") format("woff2"), url("/_astro/inter-latin-ext-400-normal.77YHD8bZ.woff") format("woff");
  unicode-range: U+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: Inter;
  font-style: normal;
  font-display: swap;
  font-weight: 400;
  src: url("/_astro/inter-latin-400-normal.C38fXH4l.woff2") format("woff2"), url("/_astro/inter-latin-400-normal.CyCys3Eg.woff") format("woff");
  unicode-range: U+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, U+329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  --background: #1a4c79;
  --foreground: #ffffffe6;
  --az-grid-bg: #1a4c79;
  --az-grid-line: #00c2ff33;
  --az-grid-line-bg: #00c2ff14;
  --darkpink: #ff72ff;
  --white-90: #ffffffe6;
  --white-70: #ffffffb3;
  --lightblue: #00c2ff;
  --green: #5ede8c;
}

@media (min-width: 360px) {
  .bg-az-grid {
    background-size: 20px 20px;
  }

  .text-title {
    font-size: 1.25rem;
  }

  .text-subtitle {
    font-size: 1.125rem;
  }

  .text-body {
    font-size: .9375rem;
  }

  .text-muted {
    font-size: .875rem;
  }

  .text-emphasis {
    font-size: .8125rem;
  }
}

@media (min-width: 440px) {
  .bg-az-grid {
    background-size: 25px 25px;
  }

  .text-title {
    font-size: 1.5rem;
  }

  .text-subtitle {
    font-size: 1.25rem;
  }

  .text-body {
    font-size: 1rem;
  }

  .text-muted {
    font-size: .9375rem;
  }

  .text-emphasis {
    font-size: .875rem;
  }
}

@media (min-width: 600px) {
  .text-title {
    font-size: 1.875rem;
  }

  .text-subtitle {
    font-size: 1.5rem;
  }

  .text-body {
    font-size: 1.0625rem;
  }

  .text-muted {
    font-size: 1rem;
  }

  .text-emphasis {
    font-size: .9375rem;
  }
}

@media (min-width: 1200px) {
  .text-title {
    font-size: 2.5rem;
  }

  .text-subtitle {
    font-size: 1.875rem;
  }

  .text-body {
    font-size: 1.125rem;
  }

  .text-muted {
    font-size: 1.0625rem;
  }

  .text-emphasis {
    font-size: 1rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}

@property --tw-rotate-x {
  syntax: "*";
  inherits: false
}

@property --tw-rotate-y {
  syntax: "*";
  inherits: false
}

@property --tw-rotate-z {
  syntax: "*";
  inherits: false
}

@property --tw-skew-x {
  syntax: "*";
  inherits: false
}

@property --tw-skew-y {
  syntax: "*";
  inherits: false
}

@property --tw-border-style {
  syntax: "*";
  inherits: false;
  initial-value: solid;
}

@property --tw-blur {
  syntax: "*";
  inherits: false
}

@property --tw-brightness {
  syntax: "*";
  inherits: false
}

@property --tw-contrast {
  syntax: "*";
  inherits: false
}

@property --tw-grayscale {
  syntax: "*";
  inherits: false
}

@property --tw-hue-rotate {
  syntax: "*";
  inherits: false
}

@property --tw-invert {
  syntax: "*";
  inherits: false
}

@property --tw-opacity {
  syntax: "*";
  inherits: false
}

@property --tw-saturate {
  syntax: "*";
  inherits: false
}

@property --tw-sepia {
  syntax: "*";
  inherits: false
}

@property --tw-drop-shadow {
  syntax: "*";
  inherits: false
}

@property --tw-drop-shadow-color {
  syntax: "*";
  inherits: false
}

@property --tw-drop-shadow-alpha {
  syntax: "<percentage>";
  inherits: false;
  initial-value: 100%;
}

@property --tw-drop-shadow-size {
  syntax: "*";
  inherits: false
}

@property --tw-backdrop-blur {
  syntax: "*";
  inherits: false
}

@property --tw-backdrop-brightness {
  syntax: "*";
  inherits: false
}

@property --tw-backdrop-contrast {
  syntax: "*";
  inherits: false
}

@property --tw-backdrop-grayscale {
  syntax: "*";
  inherits: false
}

@property --tw-backdrop-hue-rotate {
  syntax: "*";
  inherits: false
}

@property --tw-backdrop-invert {
  syntax: "*";
  inherits: false
}

@property --tw-backdrop-opacity {
  syntax: "*";
  inherits: false
}

@property --tw-backdrop-saturate {
  syntax: "*";
  inherits: false
}

@property --tw-backdrop-sepia {
  syntax: "*";
  inherits: false
}

@property --tw-ease {
  syntax: "*";
  inherits: false
}
.badge {
  display: inline-flex;
  align-items: center;
  padding: 0.3rem 0.7rem;
  border-radius: 9999px;
  border: 1px solid var(--darkpink);
  background-color: color-mix(in srgb, var(--background) 70%, transparent);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  color: var(--white-90);
  font-family: var(--font-plex);
  font-weight: 600;
  font-size: 0.7rem;
  letter-spacing: 0.03em;
  white-space: nowrap;
}

@media (min-width: 440px) {
  .badge {
    font-size: 0.8rem;
    padding: 0.35rem 0.85rem;
  }
}

@media (min-width: 1200px) {
  .badge {
    font-size: 0.85rem;
    padding: 0.4rem 0.95rem;
  }
}
/* Secuencia de carga inicial (una sola vez, 1.4s): el logo aparece con opacidad
   suave, laten los 4 puntos mientras "carga", y se va. A partir de ahí el dock
   (NavDock.css) y el selector de idioma (LanguageSelector.css) continúan la
   coreografía con sus propios delays, encadenados a estos mismos tiempos. */
.boot-intro {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.25rem;
  pointer-events: none;
  opacity: 0;
  animation: boot-intro-lifecycle 1400ms forwards;
}

.boot-intro__logo {
  width: 4rem;
  height: 4rem;
  fill: var(--darkpink);
}

.boot-intro__dots {
  display: flex;
  gap: 0.5rem;
}

.boot-intro__dot {
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 9999px;
  background-color: var(--darkpink);
  animation: boot-intro-dot-pulse 600ms ease-in-out infinite;
}

.boot-intro__dot:nth-child(2) {
  animation-delay: 150ms;
}

.boot-intro__dot:nth-child(3) {
  animation-delay: 300ms;
}

.boot-intro__dot:nth-child(4) {
  animation-delay: 450ms;
}

@keyframes boot-intro-dot-pulse {
  0%,
  100% {
    transform: scale(1);
    opacity: 0.35;
  }
  50% {
    transform: scale(1.4);
    opacity: 1;
  }
}

/* 0-21% (~0.3s): entra suave. 21-79% (~0.8s): visible, laten los puntos.
   79-100% (~0.3s): se va y queda oculto (forwards + visibility:hidden). */
@keyframes boot-intro-lifecycle {
  0% {
    opacity: 0;
  }
  21% {
    opacity: 1;
  }
  79% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    visibility: hidden;
  }
}

@media (min-width: 440px) {
  .boot-intro__logo {
    width: 5rem;
    height: 5rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  .boot-intro {
    display: none;
  }
}
.floating-leaves {
  position: fixed;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 0;
}

/* Capa 1: caída vertical, individual por hoja (translate y, opacity). Sin
   componente horizontal propio: el desplazamiento a los lados y la inclinación
   del cuerpo salen ÚNICAMENTE de la Capa 2 (viento compartido), para que el
   viento se sienta como una sola fuerza global y no como cada hoja arrastrando
   su propio recorrido lateral por separado. Lo individual de cada hoja queda en
   su velocidad de caída (--duration), su momento de aparición (--delay), su
   tamaño, cuánto le pega el viento (--wind-sensitivity, ver Capa 2) y su aleteo
   local (Capa 3): mismo viento para todas, pero ninguna se mueve exactamente igual. */
.floating-leaves__leaf {
  position: absolute;
  top: 0;
  left: calc(var(--x, 0) * 1%);
  opacity: 0;
  animation-name: leaf-fall, wind-gust;
  animation-duration: var(--duration, 26s), 46s;
  animation-timing-function: linear, linear;
  animation-iteration-count: infinite, infinite;
  animation-delay: var(--delay, 0s), 0s;
  animation-fill-mode: backwards, none;
  will-change: translate, rotate, transform, opacity;
}

/* Las 14 hojas son idénticas en HTML (mismo markup, sin atributos por instancia):
   lo que las distingue es puramente CSS, una regla por posición vía :nth-child.
   Los valores salen de la misma fórmula determinística de siempre, calculada una
   sola vez y pegada acá como reglas normales, no generada en el HTML en cada build. */
.floating-leaves__leaf:nth-child(1) {
  --x: 73.33;
  --duration: 22s;
  --delay: 0s;
  --size: 0.6;
  --leaf-opacity: 0.14;
  --wind-sensitivity: 0.65;
}
.floating-leaves__leaf:nth-child(1) .floating-leaves__leaf-flutter {
  --flutter-duration: 9s;
  --flutter-delay: 0s;
  --flutter-radius: 0.35rem;
}
.floating-leaves__leaf:nth-child(2) {
  --x: 71;
  --duration: 27s;
  --delay: 4s;
  --size: 1.3;
  --leaf-opacity: 0.17;
  --wind-sensitivity: 1.2;
}
.floating-leaves__leaf:nth-child(2) .floating-leaves__leaf-flutter {
  --flutter-duration: 12s;
  --flutter-delay: 7s;
  --flutter-radius: 1.05rem;
}
.floating-leaves__leaf:nth-child(3) {
  --x: 81;
  --duration: 32s;
  --delay: 8s;
  --size: 1;
  --leaf-opacity: 0.2;
  --wind-sensitivity: 1.15;
}
.floating-leaves__leaf:nth-child(3) .floating-leaves__leaf-flutter {
  --flutter-duration: 15s;
  --flutter-delay: 8s;
  --flutter-radius: 0.85rem;
}
.floating-leaves__leaf:nth-child(4) {
  --x: 36.67;
  --duration: 37s;
  --delay: 12s;
  --size: 0.7;
  --leaf-opacity: 0.15;
  --wind-sensitivity: 1.1;
}
.floating-leaves__leaf:nth-child(4) .floating-leaves__leaf-flutter {
  --flutter-duration: 18s;
  --flutter-delay: 3s;
  --flutter-radius: 0.65rem;
}
.floating-leaves__leaf:nth-child(5) {
  --x: 54.67;
  --duration: 26s;
  --delay: 16s;
  --size: 1.4;
  --leaf-opacity: 0.18;
  --wind-sensitivity: 1.05;
}
.floating-leaves__leaf:nth-child(5) .floating-leaves__leaf-flutter {
  --flutter-duration: 11s;
  --flutter-delay: 10s;
  --flutter-radius: 0.45rem;
}
.floating-leaves__leaf:nth-child(6) {
  --x: 85;
  --duration: 31s;
  --delay: 15s;
  --size: 1.1;
  --leaf-opacity: 0.21;
  --wind-sensitivity: 1;
}
.floating-leaves__leaf:nth-child(6) .floating-leaves__leaf-flutter {
  --flutter-duration: 14s;
  --flutter-delay: 11s;
  --flutter-radius: 1.15rem;
}
.floating-leaves__leaf:nth-child(7) {
  --x: 22;
  --duration: 36s;
  --delay: 19s;
  --size: 0.8;
  --leaf-opacity: 0.16;
  --wind-sensitivity: 0.95;
}
.floating-leaves__leaf:nth-child(7) .floating-leaves__leaf-flutter {
  --flutter-duration: 17s;
  --flutter-delay: 12s;
  --flutter-radius: 0.95rem;
}
.floating-leaves__leaf:nth-child(8) {
  --x: 59;
  --duration: 25s;
  --delay: 23s;
  --size: 1.5;
  --leaf-opacity: 0.19;
  --wind-sensitivity: 0.9;
}
.floating-leaves__leaf:nth-child(8) .floating-leaves__leaf-flutter {
  --flutter-duration: 10s;
  --flutter-delay: 3s;
  --flutter-radius: 0.75rem;
}
.floating-leaves__leaf:nth-child(9) {
  --x: 96;
  --duration: 30s;
  --delay: 27s;
  --size: 1.2;
  --leaf-opacity: 0.14;
  --wind-sensitivity: 0.85;
}
.floating-leaves__leaf:nth-child(9) .floating-leaves__leaf-flutter {
  --flutter-duration: 13s;
  --flutter-delay: 9s;
  --flutter-radius: 0.55rem;
}
.floating-leaves__leaf:nth-child(10) {
  --x: 33;
  --duration: 35s;
  --delay: 31s;
  --size: 0.9;
  --leaf-opacity: 0.17;
  --wind-sensitivity: 0.8;
}
.floating-leaves__leaf:nth-child(10) .floating-leaves__leaf-flutter {
  --flutter-duration: 16s;
  --flutter-delay: 11s;
  --flutter-radius: 0.35rem;
}
.floating-leaves__leaf:nth-child(11) {
  --x: 70;
  --duration: 24s;
  --delay: 30s;
  --size: 0.6;
  --leaf-opacity: 0.2;
  --wind-sensitivity: 0.75;
}
.floating-leaves__leaf:nth-child(11) .floating-leaves__leaf-flutter {
  --flutter-duration: 9s;
  --flutter-delay: 1s;
  --flutter-radius: 1.05rem;
}
.floating-leaves__leaf:nth-child(12) {
  --x: 7;
  --duration: 29s;
  --delay: 34s;
  --size: 1.3;
  --leaf-opacity: 0.15;
  --wind-sensitivity: 0.7;
}
.floating-leaves__leaf:nth-child(12) .floating-leaves__leaf-flutter {
  --flutter-duration: 12s;
  --flutter-delay: 5s;
  --flutter-radius: 0.85rem;
}
.floating-leaves__leaf:nth-child(13) {
  --x: 44;
  --duration: 34s;
  --delay: 38s;
  --size: 1;
  --leaf-opacity: 0.18;
  --wind-sensitivity: 0.65;
}
.floating-leaves__leaf:nth-child(13) .floating-leaves__leaf-flutter {
  --flutter-duration: 15s;
  --flutter-delay: 3s;
  --flutter-radius: 0.65rem;
}
.floating-leaves__leaf:nth-child(14) {
  --x: 81;
  --duration: 23s;
  --delay: 42s;
  --size: 0.7;
  --leaf-opacity: 0.21;
  --wind-sensitivity: 1.2;
}
.floating-leaves__leaf:nth-child(14) .floating-leaves__leaf-flutter {
  --flutter-duration: 18s;
  --flutter-delay: 13s;
  --flutter-radius: 0.45rem;
}

@keyframes leaf-fall {
  0% {
    translate: 0 -10vh;
    opacity: 0;
  }
  6% {
    opacity: var(--leaf-opacity, 0.16);
  }
  94% {
    opacity: var(--leaf-opacity, 0.16);
  }
  100% {
    translate: 0 112vh;
    opacity: 0;
  }
}

/* Capa 2: viento AMBIENTAL, una sola animación fija (sin variar por hoja) aplicada
   por igual a todas: por eso todas las hojas en pantalla se mueven juntas, en la
   MISMA dirección y en el MISMO instante (no cada una arrastrando su propio
   recorrido lateral). Pero no es igual de intensa para todas: cada valor está
   multiplicado por `--wind-sensitivity` (distinta por hoja, como si algunas
   pesaran menos que otras), así que la dirección y el momento son compartidos,
   pero cuánto se mueve y cuánto se inclina cada una no es idéntico.
   `transform` para el desplazamiento x/y (independiente de `translate`, que en la
   Capa 1 ya controla la caída) y `rotate` para la inclinación del cuerpo, calculada
   a partir del MISMO valor de viento (rotate = x_viento / 23 * 30deg), así que el
   cuerpo se recuesta exactamente hacia donde sopla en cada instante.
   Los valores base son una onda seno pura, value(t) = 23vw * sin(2*pi*t), muestreada
   cada 5%: una función seno es infinitamente derivable (nunca tiene un quiebre de
   velocidad NI de aceleración en ningún punto). La velocidad es máxima al cruzar
   por el centro (ahí "cambia de dirección" el viento) y mínima en los picos, igual
   que un péndulo real: nunca queda fija, nunca frena de golpe, nunca acelera de golpe.
   En cada pico (25% y 75%, uno hacia la derecha y otro hacia la izquierda) se suma
   un envión hacia arriba (-11vh en la base, también escalado por sensibilidad),
   moderado para que nunca gane con fuerza contra la caída propia de la hoja y se
   sienta como un planeo, no como un salto. Ese envión es también un coseno
   elevado, así que entra y sale con velocidad cero, igual de suave que el resto. */
@keyframes wind-gust {
  0% {
    transform: translate(0, 0);
    rotate: 0deg;
  }
  5% {
    transform: translate(calc(7.1vw * var(--wind-sensitivity, 1)), 0);
    rotate: calc(9deg * var(--wind-sensitivity, 1));
  }
  10% {
    transform: translate(calc(13.5vw * var(--wind-sensitivity, 1)), 0);
    rotate: calc(18deg * var(--wind-sensitivity, 1));
  }
  15% {
    transform: translate(calc(18.6vw * var(--wind-sensitivity, 1)), 0);
    rotate: calc(24deg * var(--wind-sensitivity, 1));
  }
  17% {
    transform: translate(calc(20.1vw * var(--wind-sensitivity, 1)), calc(-1vh * var(--wind-sensitivity, 1)));
    rotate: calc(26deg * var(--wind-sensitivity, 1));
  }
  19% {
    transform: translate(calc(21.4vw * var(--wind-sensitivity, 1)), calc(-3.8vh * var(--wind-sensitivity, 1)));
    rotate: calc(28deg * var(--wind-sensitivity, 1));
  }
  21% {
    transform: translate(calc(22.3vw * var(--wind-sensitivity, 1)), calc(-7.2vh * var(--wind-sensitivity, 1)));
    rotate: calc(29deg * var(--wind-sensitivity, 1));
  }
  23% {
    transform: translate(calc(22.8vw * var(--wind-sensitivity, 1)), calc(-9.9vh * var(--wind-sensitivity, 1)));
    rotate: calc(30deg * var(--wind-sensitivity, 1));
  }
  25% {
    transform: translate(calc(23vw * var(--wind-sensitivity, 1)), calc(-11vh * var(--wind-sensitivity, 1)));
    rotate: calc(30deg * var(--wind-sensitivity, 1));
  }
  27% {
    transform: translate(calc(22.8vw * var(--wind-sensitivity, 1)), calc(-9.9vh * var(--wind-sensitivity, 1)));
    rotate: calc(30deg * var(--wind-sensitivity, 1));
  }
  29% {
    transform: translate(calc(22.3vw * var(--wind-sensitivity, 1)), calc(-7.2vh * var(--wind-sensitivity, 1)));
    rotate: calc(29deg * var(--wind-sensitivity, 1));
  }
  31% {
    transform: translate(calc(21.4vw * var(--wind-sensitivity, 1)), calc(-3.8vh * var(--wind-sensitivity, 1)));
    rotate: calc(28deg * var(--wind-sensitivity, 1));
  }
  33% {
    transform: translate(calc(20.1vw * var(--wind-sensitivity, 1)), calc(-1vh * var(--wind-sensitivity, 1)));
    rotate: calc(26deg * var(--wind-sensitivity, 1));
  }
  35% {
    transform: translate(calc(18.6vw * var(--wind-sensitivity, 1)), 0);
    rotate: calc(24deg * var(--wind-sensitivity, 1));
  }
  40% {
    transform: translate(calc(13.5vw * var(--wind-sensitivity, 1)), 0);
    rotate: calc(18deg * var(--wind-sensitivity, 1));
  }
  45% {
    transform: translate(calc(7.1vw * var(--wind-sensitivity, 1)), 0);
    rotate: calc(9deg * var(--wind-sensitivity, 1));
  }
  50% {
    transform: translate(0, 0);
    rotate: 0deg;
  }
  55% {
    transform: translate(calc(-7.1vw * var(--wind-sensitivity, 1)), 0);
    rotate: calc(-9deg * var(--wind-sensitivity, 1));
  }
  60% {
    transform: translate(calc(-13.5vw * var(--wind-sensitivity, 1)), 0);
    rotate: calc(-18deg * var(--wind-sensitivity, 1));
  }
  65% {
    transform: translate(calc(-18.6vw * var(--wind-sensitivity, 1)), 0);
    rotate: calc(-24deg * var(--wind-sensitivity, 1));
  }
  67% {
    transform: translate(calc(-20.1vw * var(--wind-sensitivity, 1)), calc(-1vh * var(--wind-sensitivity, 1)));
    rotate: calc(-26deg * var(--wind-sensitivity, 1));
  }
  69% {
    transform: translate(calc(-21.4vw * var(--wind-sensitivity, 1)), calc(-3.8vh * var(--wind-sensitivity, 1)));
    rotate: calc(-28deg * var(--wind-sensitivity, 1));
  }
  71% {
    transform: translate(calc(-22.3vw * var(--wind-sensitivity, 1)), calc(-7.2vh * var(--wind-sensitivity, 1)));
    rotate: calc(-29deg * var(--wind-sensitivity, 1));
  }
  73% {
    transform: translate(calc(-22.8vw * var(--wind-sensitivity, 1)), calc(-9.9vh * var(--wind-sensitivity, 1)));
    rotate: calc(-30deg * var(--wind-sensitivity, 1));
  }
  75% {
    transform: translate(calc(-23vw * var(--wind-sensitivity, 1)), calc(-11vh * var(--wind-sensitivity, 1)));
    rotate: calc(-30deg * var(--wind-sensitivity, 1));
  }
  77% {
    transform: translate(calc(-22.8vw * var(--wind-sensitivity, 1)), calc(-9.9vh * var(--wind-sensitivity, 1)));
    rotate: calc(-30deg * var(--wind-sensitivity, 1));
  }
  79% {
    transform: translate(calc(-22.3vw * var(--wind-sensitivity, 1)), calc(-7.2vh * var(--wind-sensitivity, 1)));
    rotate: calc(-29deg * var(--wind-sensitivity, 1));
  }
  81% {
    transform: translate(calc(-21.4vw * var(--wind-sensitivity, 1)), calc(-3.8vh * var(--wind-sensitivity, 1)));
    rotate: calc(-28deg * var(--wind-sensitivity, 1));
  }
  83% {
    transform: translate(calc(-20.1vw * var(--wind-sensitivity, 1)), calc(-1vh * var(--wind-sensitivity, 1)));
    rotate: calc(-26deg * var(--wind-sensitivity, 1));
  }
  85% {
    transform: translate(calc(-18.6vw * var(--wind-sensitivity, 1)), 0);
    rotate: calc(-24deg * var(--wind-sensitivity, 1));
  }
  90% {
    transform: translate(calc(-13.5vw * var(--wind-sensitivity, 1)), 0);
    rotate: calc(-18deg * var(--wind-sensitivity, 1));
  }
  95% {
    transform: translate(calc(-7.1vw * var(--wind-sensitivity, 1)), 0);
    rotate: calc(-9deg * var(--wind-sensitivity, 1));
  }
  100% {
    transform: translate(0, 0);
    rotate: 0deg;
  }
}

/* Capa 3: aleteo/loop local, independiente de la caída. La mayor parte del ciclo está
   quieta; solo una ventana amplia y lenta traza un círculo completo (con giro sincronizado),
   como una ráfaga puntual que atrapa la hoja y la suelta donde la encontró. */
.floating-leaves__leaf-flutter {
  animation: leaf-flutter var(--flutter-duration, 12s) linear infinite;
  animation-delay: var(--flutter-delay, 0s);
  will-change: translate, rotate;
}

/* Mismo criterio que la Capa 1: todo `linear`, entrada y salida del giro repartidas
   en varios pasos que crecen/decrecen de a poco (56-65% y 90-100%), en vez de un
   salto directo de reposo a giro completo. */
@keyframes leaf-flutter {
  0%,
  50% {
    translate: 0 0;
    rotate: 0deg;
  }
  56% {
    translate: calc(var(--flutter-radius, 0.6rem) * 0.18) calc(var(--flutter-radius, 0.6rem) * 0.07);
    rotate: 11deg;
  }
  61% {
    translate: calc(var(--flutter-radius, 0.6rem) * 0.5) calc(var(--flutter-radius, 0.6rem) * 0.13);
    rotate: 28deg;
  }
  65% {
    translate: calc(var(--flutter-radius, 0.6rem) * 0.71) calc(var(--flutter-radius, 0.6rem) * 0.29);
    rotate: 45deg;
  }
  70% {
    translate: var(--flutter-radius, 0.6rem) var(--flutter-radius, 0.6rem);
    rotate: 90deg;
  }
  75% {
    translate: calc(var(--flutter-radius, 0.6rem) * 0.71) calc(var(--flutter-radius, 0.6rem) * 1.71);
    rotate: 135deg;
  }
  80% {
    translate: 0 calc(var(--flutter-radius, 0.6rem) * 2);
    rotate: 180deg;
  }
  85% {
    translate: calc(var(--flutter-radius, 0.6rem) * -0.71) calc(var(--flutter-radius, 0.6rem) * 1.71);
    rotate: 225deg;
  }
  90% {
    translate: calc(var(--flutter-radius, 0.6rem) * -1) var(--flutter-radius, 0.6rem);
    rotate: 270deg;
  }
  94% {
    translate: calc(var(--flutter-radius, 0.6rem) * -0.71) calc(var(--flutter-radius, 0.6rem) * 0.5);
    rotate: 300deg;
  }
  97% {
    translate: calc(var(--flutter-radius, 0.6rem) * -0.3) calc(var(--flutter-radius, 0.6rem) * 0.15);
    rotate: 330deg;
  }
  100% {
    translate: 0 0;
    rotate: 360deg;
  }
}

.floating-leaves__leaf-svg {
  display: block;
  width: calc(var(--size, 1) * 1.1rem);
  height: calc(var(--size, 1) * 1.1rem);
  color: var(--darkpink);
}

.floating-leaves__leaf-body {
  fill: currentColor;
}

.floating-leaves__leaf-vein {
  fill: none;
  stroke: color-mix(in srgb, currentColor 65%, black 35%);
  stroke-width: 0.6;
  stroke-linecap: round;
}

/* Las 14 hojas se ven en todos los breakpoints (sin recorte por mobile). */
@media (min-width: 440px) {
  .floating-leaves__leaf-svg {
    width: calc(var(--size, 1) * 1.25rem);
    height: calc(var(--size, 1) * 1.25rem);
  }
}

@media (min-width: 1200px) {
  .floating-leaves__leaf-svg {
    width: calc(var(--size, 1) * 1.4rem);
    height: calc(var(--size, 1) * 1.4rem);
  }
}

@media (prefers-reduced-motion: reduce) {
  .floating-leaves {
    display: none;
  }
}
/* 3 estados excluyentes controlados por una clase en la raíz (trigger,
   captcha, revelado): cada bloque interno arranca oculto y solo el estado
   activo lo muestra, así no hace falta tocar `hidden` a mano por elemento. */
.email-reveal {
  display: flex;
  justify-content: center;
}

.email-reveal__trigger,
.email-reveal__captcha,
.email-reveal__result {
  display: none;
}

.email-reveal__trigger {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.6rem 1.1rem;
  border-radius: 9999px;
  border: 1px solid color-mix(in srgb, var(--lightblue) 50%, transparent);
  background: none;
  color: var(--white-90);
  font-family: var(--font-plex);
  font-size: 0.8125rem;
  letter-spacing: 0.02em;
  cursor: pointer;
  transition: border-color 150ms ease, color 150ms ease;
}

.email-reveal__trigger:hover,
.email-reveal__trigger:focus-visible {
  border-color: var(--lightblue);
  color: var(--lightblue);
  outline: none;
}

.email-reveal__trigger-icon {
  width: 1rem;
  height: 1rem;
}

.email-reveal--captcha .email-reveal__trigger,
.email-reveal--revealed .email-reveal__trigger {
  display: none;
}

.email-reveal--captcha .email-reveal__captcha {
  display: flex;
}

/* ── Captcha: mismo espíritu "terminal/CRT" que el MathCaptcha de
   modus-ratio (dígitos en casillas con glow monoespaciado), adaptado a los
   tokens de este proyecto en vez de sus `--ch-*`. Acá los dígitos son texto
   normal (no canvas): ese anillo era anti-OCR para gatear un submit real,
   acá el objetivo es solo fricción anti-bot + look, no hace falta esa
   capa extra. */
.email-reveal__captcha {
  flex-direction: column;
  align-items: center;
  gap: 0.4rem;
  padding: 0.85rem 1rem;
  border: 1px solid color-mix(in srgb, var(--lightblue) 35%, transparent);
  border-radius: 0.5rem;
  background-color: color-mix(in srgb, var(--background) 80%, transparent);
  transition: border-color 200ms ease, background-color 200ms ease;
}

.email-reveal--valid .email-reveal__captcha {
  border-color: var(--green);
  background-color: color-mix(in srgb, var(--green) 10%, transparent);
}

.email-reveal--error .email-reveal__captcha {
  border-color: var(--darkpink);
  animation: email-reveal-shake 400ms ease;
}

@keyframes email-reveal-shake {
  0%,
  100% {
    transform: translateX(0);
  }
  20% {
    transform: translateX(-4px);
  }
  40% {
    transform: translateX(4px);
  }
  60% {
    transform: translateX(-4px);
  }
  80% {
    transform: translateX(4px);
  }
}

.email-reveal__captcha-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.email-reveal__captcha-input {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 3.25rem;
  font-family: var(--font-plex);
  font-size: 1.25rem;
  font-weight: 700;
  line-height: 1;
}

/* El dígito en sí (fondo/borde/scanlines/glow) lo pinta el <script> DENTRO
   del canvas, como bitmap: no queda ningún nodo de texto seleccionable ni
   parseable por un bot. Acá solo el tamaño de despliegue y un glow externo
   sutil (`filter`, no `text-shadow`, porque no hay texto real que
   sombrear). */
.email-reveal__digit {
  display: block;
  width: 2.75rem;
  height: 3.25rem;
  border-radius: 0.375rem;
  filter: drop-shadow(0 0 4px color-mix(in srgb, var(--lightblue) 35%, transparent));
}

.email-reveal__op {
  font-family: var(--font-plex);
  font-size: 1.1rem;
  font-weight: 700;
  color: color-mix(in srgb, var(--lightblue) 70%, transparent);
  flex-shrink: 0;
}

.email-reveal__captcha-input {
  padding: 0;
  border: 1.5px solid color-mix(in srgb, var(--lightblue) 45%, transparent);
  border-radius: 0.375rem;
  background-color: color-mix(in srgb, var(--lightblue) 6%, transparent);
  color: var(--white-90);
  text-align: center;
  appearance: textfield;
  -moz-appearance: textfield;
  transition: border-color 150ms ease, box-shadow 150ms ease;
}

.email-reveal__captcha-input::-webkit-outer-spin-button,
.email-reveal__captcha-input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.email-reveal__captcha-input:focus {
  outline: none;
  border-color: var(--lightblue);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--lightblue) 20%, transparent);
}

.email-reveal--valid .email-reveal__captcha-input {
  border-color: var(--green);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--green) 20%, transparent);
}

.email-reveal--error .email-reveal__captcha-input {
  border-color: var(--darkpink);
  box-shadow: 0 0 0 2px color-mix(in srgb, var(--darkpink) 20%, transparent);
}

.email-reveal__captcha-input:disabled {
  opacity: 0.7;
  cursor: not-allowed;
}

.email-reveal__captcha-check {
  display: none;
  width: 1.5rem;
  height: 1.5rem;
  color: var(--green);
  flex-shrink: 0;
}

.email-reveal--valid .email-reveal__captcha-check {
  display: block;
}

.email-reveal__captcha-retry {
  display: none;
  font-size: 0.75rem;
  color: var(--darkpink);
  white-space: nowrap;
}

.email-reveal--error .email-reveal__captcha-retry {
  display: inline;
}

/* ── Resultado (correo revelado) ── */
.email-reveal--revealed .email-reveal__result {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  justify-content: center;
}

.email-reveal__email-text {
  padding: 0.5rem 0.85rem;
  border: 1px solid color-mix(in srgb, var(--green) 40%, transparent);
  border-radius: 0.5rem;
  background-color: color-mix(in srgb, var(--green) 8%, transparent);
  color: var(--white-90);
  font-family: var(--font-plex);
  font-size: 0.8125rem;
  -webkit-user-select: all;
  user-select: all;
}

.email-reveal__action {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 9999px;
  border: 1px solid color-mix(in srgb, var(--lightblue) 50%, transparent);
  background: none;
  color: var(--white-70);
  cursor: pointer;
  transition: border-color 150ms ease, background-color 150ms ease, color 150ms ease;
}

.email-reveal__action:hover,
.email-reveal__action:focus-visible {
  border-color: var(--lightblue);
  color: var(--lightblue);
  outline: none;
}

.email-reveal__action svg {
  width: 1.1rem;
  height: 1.1rem;
}

.email-reveal__copied-icon {
  display: none;
}

.email-reveal--copied [data-action='copy'] {
  border-color: var(--green);
  color: var(--green);
}

.email-reveal--copied .email-reveal__copy-icon {
  display: none;
}

.email-reveal--copied .email-reveal__copied-icon {
  display: block;
}

.email-reveal__copied-label {
  display: none;
  font-size: 0.75rem;
  color: var(--green);
  white-space: nowrap;
}

.email-reveal--copied .email-reveal__copied-label {
  display: inline;
}

@media (prefers-reduced-motion: reduce) {
  .email-reveal--error .email-reveal__captcha {
    animation: none;
  }
}
.experience-card {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  width: 100%;
  padding: 1rem;
  border-radius: 0.75rem;
  border: 1px solid var(--az-grid-line);
  background-color: color-mix(in srgb, var(--background) 70%, transparent);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--darkpink) 15%, transparent),
    0 1rem 2rem -1.25rem rgba(0, 0, 0, 0.6);
}

.experience-card__dates {
  font-family: var(--font-plex);
  font-weight: 600;
  font-size: 0.75rem;
  letter-spacing: 0.03em;
  color: var(--lightblue);
}

.experience-card__company {
  font-family: var(--font-encode);
  font-weight: 700;
  color: var(--white-90);
  font-size: 1rem;
  line-height: 1.25;
}

.experience-card__role {
  font-family: var(--font-encode);
  font-weight: 500;
  color: var(--white-70);
  font-size: 0.8125rem;
  line-height: 1.3;
}

.experience-card__responsibilities {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  margin-top: 0.15rem;
  padding-left: 1rem;
  font-size: 0.75rem;
}

.experience-card__responsibilities li {
  list-style: disc;
}

.experience-card__badges {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-top: 0.25rem;
}

@media (min-width: 440px) {
  .experience-card__company {
    font-size: 1.125rem;
  }

  .experience-card__role {
    font-size: 0.875rem;
  }

  .experience-card__responsibilities {
    font-size: 0.8125rem;
  }
}
/* Última pieza de toda la secuencia de entrada (BootIntro -> NavDock -> Banner
   completo -> acá): aparece recién cuando ya terminaron de aparecer los
   bloques de cifras del Banner (~4.95s). */
/* En mobile/tablet va a la derecha (right, no left): como el panel ES/EN
   crece hacia la derecha del ícono, anclar por `right` hace que el pill se
   expanda hacia la izquierda (dentro de la pantalla) en vez de salirse por
   el borde. Desde 2xl vuelve a la izquierda. */
.language-selector {
  position: fixed;
  top: 1rem;
  right: 1rem;
  z-index: 50;
  display: flex;
  align-items: center;
  padding: 0.5rem;
  border-radius: 9999px;
  border: 1px solid var(--az-grid-line);
  background-color: color-mix(in srgb, var(--background) 85%, transparent);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  overflow: hidden;
  opacity: 0;
  animation: language-selector-entrance 300ms ease-out 4950ms forwards;
  transition: border-color 150ms ease;
}

@keyframes language-selector-entrance {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

.language-selector:hover,
.language-selector:focus-within {
  border-color: var(--darkpink);
  /* Libera el overflow:hidden (necesario para el colapso) solo mientras está
     abierto: si no, el tooltip que sobresale hacia arriba queda recortado. */
  overflow: visible;
}

/* Llama la atención justo cuando termina de aparecer (4950ms + 300ms de entrada):
   3 parpadeos en rosa y se asienta en su color normal. El texto ES/EN queda
   oculto hasta el hover, así que el ícono es lo único visible para esto. */
.language-selector__icon {
  width: 1.1rem;
  height: 1.1rem;
  color: var(--white-70);
  flex-shrink: 0;
  transition: color 150ms ease;
  animation: language-selector-icon-attention 1200ms ease-in-out 5250ms 1;
}

@keyframes language-selector-icon-attention {
  0%,
  33%,
  66%,
  100% {
    color: var(--white-70);
  }
  16.5%,
  50%,
  83% {
    color: var(--darkpink);
  }
}

.language-selector:hover .language-selector__icon,
.language-selector:focus-within .language-selector__icon {
  color: var(--darkpink);
}

.language-selector__options {
  display: flex;
  gap: 0.25rem;
  max-width: 0;
  margin-left: 0;
  opacity: 0;
  overflow: hidden;
  transition: max-width 220ms ease, margin-left 220ms ease, opacity 150ms ease;
}

.language-selector:hover .language-selector__options,
.language-selector:focus-within .language-selector__options {
  max-width: 6rem;
  margin-left: 0.5rem;
  opacity: 1;
  overflow: visible;
}

.language-selector__option {
  position: relative;
  font-family: var(--font-plex);
  font-size: 0.7rem;
  letter-spacing: 0.05em;
  padding: 0.25rem 0.5rem;
  border-radius: 9999px;
  color: var(--white-70);
  background: none;
  border: 1px solid transparent;
  cursor: pointer;
  transition: color 150ms ease, border-color 150ms ease;
}

.language-selector__option:hover,
.language-selector__option:focus-visible {
  color: var(--darkpink);
  border-color: var(--darkpink);
  outline: none;
}

.language-selector__option--active {
  color: var(--darkpink);
  border-color: var(--darkpink);
}

/* Mismo tooltip que NavDock.css, pero hacia ABAJO en vez de hacia arriba:
   este selector vive pegado al borde superior de la pantalla (top: 1rem), así
   que un tooltip hacia arriba no tiene espacio y se corta contra el borde. */
.language-selector__tooltip {
  position: absolute;
  top: calc(100% + 0.6rem);
  left: 50%;
  padding: 0.3rem 0.65rem;
  border-radius: 0.375rem;
  border: 1px solid var(--darkpink);
  background-color: color-mix(in srgb, var(--background) 92%, transparent);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  color: var(--white-90);
  font-family: var(--font-plex);
  font-size: 0.7rem;
  letter-spacing: 0.02em;
  white-space: nowrap;
  opacity: 0;
  transform: translate(-50%, -0.2rem);
  transition: opacity 90ms ease, transform 90ms ease;
  pointer-events: none;
}

.language-selector__tooltip::before,
.language-selector__tooltip::after {
  content: '';
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);
  border: 5px solid transparent;
}

.language-selector__tooltip::before {
  border-bottom-color: var(--darkpink);
}

.language-selector__tooltip::after {
  bottom: calc(100% - 1px);
  border-width: 4px;
  border-bottom-color: color-mix(in srgb, var(--background) 92%, transparent);
}

.language-selector__option:hover .language-selector__tooltip,
.language-selector__option:focus-visible .language-selector__tooltip {
  opacity: 1;
  transform: translate(-50%, 0);
}

@media (min-width: 440px) {
  .language-selector {
    top: 1.25rem;
    right: 1.25rem;
    padding: 0.6rem;
  }

  .language-selector__icon {
    width: 1.25rem;
    height: 1.25rem;
  }

  .language-selector__option {
    font-size: 0.8rem;
    padding: 0.3rem 0.6rem;
  }

  .language-selector__tooltip {
    font-size: 0.75rem;
    padding: 0.35rem 0.75rem;
  }
}

@media (min-width: 1200px) {
  .language-selector {
    right: auto;
    left: 1.25rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  .language-selector,
  .language-selector__icon {
    animation: none;
    opacity: 1;
  }

  .language-selector,
  .language-selector__icon,
  .language-selector__options,
  .language-selector__option,
  .language-selector__tooltip {
    transition: none;
  }
}
/* Orden de la secuencia de carga: BootIntro -> NavDock -> Banner (acá, a los
   2.75s) -> LanguageSelector. La card es más alta que la imagen: abajo hay una
   franja tipo "footer" propia (con su fondo y borde superior) para los
   badges, en vez de superponerlos sobre la foto con un degradé. */
.photo-card {
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 21rem;
  margin: 0;
  border-radius: 1.25rem;
  overflow: hidden;
  border: 1px solid var(--az-grid-line);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--darkpink) 25%, transparent), 0 1.5rem 3rem -1.5rem rgba(0, 0, 0, 0.6);
  opacity: 0;
  animation: photo-card-entrance 400ms ease-out 2750ms forwards;
}

@keyframes photo-card-entrance {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

.photo-card__image {
  display: block;
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
}

.photo-card__badges {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  padding: 0.75rem 0.9rem;
  border-top: 1px solid var(--az-grid-line);
  background-color: color-mix(in srgb, var(--background) 92%, transparent);
}

/* Los badges aparecen uno por uno, recién cuando la card ya es visible (a
   partir de los 3.15s, justo cuando termina photo-card-entrance). Se escala
   acá (no en Badge.css) para no acoplar este timing al átomo genérico. */
.photo-card__badges .badge {
  opacity: 0;
  animation: photo-card-badge-entrance 200ms ease-out 3150ms forwards;
}

@keyframes photo-card-badge-entrance {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

.photo-card__badges .badge:nth-child(2) {
  animation-delay: 3230ms;
}

.photo-card__badges .badge:nth-child(3) {
  animation-delay: 3310ms;
}

.photo-card__badges .badge:nth-child(4) {
  animation-delay: 3390ms;
}

/* Badge extra, solo en mobile de verdad (no tablet): se ve raro tenerlo en
   pantallas más grandes, donde ya no hace falta "rellenar" tanto la fila. */
.photo-card__badge-mobile-only {
  display: inline-flex;
}

.photo-card__badge-mobile-only .badge {
  opacity: 0;
  animation: photo-card-badge-entrance 200ms ease-out 3470ms forwards;
}

@media (min-width: 600px) {
  .photo-card__badge-mobile-only {
    display: none;
  }
}

@media (min-width: 440px) {
  .photo-card {
    max-width: 23rem;
    border-radius: 1.5rem;
  }

  .photo-card__badges {
    gap: 0.5rem;
    padding: 0.9rem 1.1rem;
  }
}

@media (min-width: 1200px) {
  .photo-card {
    max-width: 27rem;
  }

  .photo-card__badges {
    padding: 1.1rem 1.25rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  .photo-card,
  .photo-card__badges .badge,
  .photo-card__badge-mobile-only .badge {
    animation: none;
    opacity: 1;
  }
}
.stat-block {
  display: flex;
  flex-direction: column;
  flex: 1 1 0;
  min-width: 0;
  gap: 0.1rem;
}

.stat-block__value {
  font-family: var(--font-encode);
  font-weight: 700;
  color: var(--darkpink);
  font-size: 0.875rem;
  line-height: 1.1;
}

.stat-block__label {
  font-size: 0.5625rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  line-height: 1.25;
}

@media (min-width: 440px) {
  .stat-block__value {
    font-size: 1rem;
  }

  .stat-block__label {
    font-size: 0.625rem;
  }
}

@media (min-width: 1200px) {
  .stat-block__value {
    font-size: 1.25rem;
  }

  .stat-block__label {
    font-size: 0.75rem;
  }
}
.tech-card {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 1rem;
  border: 1px solid var(--az-grid-line);
  border-radius: 0.5rem;
  background-color: color-mix(in srgb, var(--background) 60%, transparent);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  /* Mismo efecto de profundidad que PhotoCard: anillo rosa fino + sombra
     suave debajo, solo que a escala de mini card. */
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--darkpink) 15%, transparent),
    0 1rem 2rem -1.25rem rgba(0, 0, 0, 0.6);
  transition: border-color 200ms ease, box-shadow 200ms ease;
}

.tech-card:hover {
  border-color: var(--darkpink);
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--darkpink) 35%, transparent),
    0 1rem 2rem -1.25rem rgba(0, 0, 0, 0.6);
}

/* IoT: en reposo se ve igual que las demás cards. Solo el ícono (la hojita)
   es verde siempre; el borde pasa a verde (en vez de rosa) solo en hover. En
   dispositivos sin hover (mobile/touch) no hay forma de "pasar el mouse", así
   que ahí se deja el borde verde de una vez. */
.tech-card--accent-green:hover {
  border-color: var(--green);
  box-shadow:
    0 0 0 1px color-mix(in srgb, var(--green) 35%, transparent),
    0 1rem 2rem -1.25rem rgba(0, 0, 0, 0.6);
}

@media (hover: none) {
  .tech-card--accent-green {
    border-color: color-mix(in srgb, var(--green) 55%, transparent);
    box-shadow:
      0 0 0 1px color-mix(in srgb, var(--green) 20%, transparent),
      0 1rem 2rem -1.25rem rgba(0, 0, 0, 0.6);
  }
}

.tech-card--accent-green .tech-card__icon {
  color: var(--green);
}

.tech-card__icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  color: var(--lightblue);
}

.tech-card__icon svg {
  width: 100%;
  height: 100%;
}

.tech-card__title {
  font-family: var(--font-encode);
  font-weight: 600;
  color: var(--white-90);
  font-size: 0.875rem;
  line-height: 1.3;
}

.tech-card__description {
  font-size: 0.75rem;
  line-height: 1.5;
}

@media (min-width: 440px) {
  .tech-card {
    padding: 1.25rem;
  }

  .tech-card__icon {
    width: 2.25rem;
    height: 2.25rem;
  }

  .tech-card__title {
    font-size: 1rem;
  }

  .tech-card__description {
    font-size: 0.8125rem;
  }
}
/* Se revela al hacer scroll (IntersectionObserver en el <script> del
   componente agrega `.about-section--visible`), no es parte de la
   orquestación de carga inicial. Orden de aparición: primero el contenido de
   la izquierda, luego (con un pequeño retraso) las cards de la derecha. */
.about-section {
  display: flex;
  flex-direction: column;
  gap: 2.5rem;
  max-width: 90rem;
  margin: 0 auto;
  /* Poco padding-top en mobile: sumado al padding-bottom del banner, el hueco
     antes de "Sobre Mí" quedaba enorme. */
  padding: 0.25rem 1.25rem 4rem;
}

.about-section__content {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  opacity: 0;
  transform: translateX(-2rem);
  transition: opacity 600ms ease-out, transform 600ms ease-out;
}

.about-section__cards {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 1rem;
  opacity: 0;
  transform: translateX(2rem);
  transition: opacity 600ms ease-out 220ms, transform 600ms ease-out 220ms;
}

.about-section--visible .about-section__content,
.about-section--visible .about-section__cards {
  opacity: 1;
  transform: translateX(0);
}

.about-section__eyebrow {
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

/* Envoltorio clickeable (easter egg del "¡Clap!" de acá abajo): sin
   fondo/borde propio, solo hace de ancla posicionada para las 8 posiciones
   fijas del texto flotante. */
.about-section__emoji-wrap {
  position: relative;
  display: block;
  width: 1.75rem;
  height: 1.75rem;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
}

/* Ícono de líneas (no emoji a color): mismo lenguaje visual que el resto de
   los íconos del sitio (stroke, sin relleno). Alineado a la izquierda, igual
   que el resto del contenido (mobile y desktop). Vaivén sutil tipo saludo,
   recién cuando la sección ya es visible. */
.about-section__emoji {
  display: block;
  width: 100%;
  height: 100%;
  color: var(--darkpink);
  transform-origin: bottom center;
}

.about-section--visible .about-section__emoji {
  animation: about-emoji-wave 1400ms linear infinite;
}

/* Onda seno real (12deg * sin(2*pi*t)), muestreada cada 1/12 y con `linear`
   entre puntos: con solo 3 paradas y `ease-in-out` la velocidad llegaba a
   cero en cada una, y se sentía "se mueve, se para, se mueve, se para" en
   vez de un vaivén fluido. */
@keyframes about-emoji-wave {
  0% {
    transform: rotate(0deg);
  }
  8.33% {
    transform: rotate(6deg);
  }
  16.67% {
    transform: rotate(10.39deg);
  }
  25% {
    transform: rotate(12deg);
  }
  33.33% {
    transform: rotate(10.39deg);
  }
  41.67% {
    transform: rotate(6deg);
  }
  50% {
    transform: rotate(0deg);
  }
  58.33% {
    transform: rotate(-6deg);
  }
  66.67% {
    transform: rotate(-10.39deg);
  }
  75% {
    transform: rotate(-12deg);
  }
  83.33% {
    transform: rotate(-10.39deg);
  }
  91.67% {
    transform: rotate(-6deg);
  }
  100% {
    transform: rotate(0deg);
  }
}

/* Easter egg: cada clic crea uno de estos, en una de las 8 posiciones fijas
   (elegida al azar por el <script>) alrededor de la mano, y se borra solo a
   los 900ms. Con varios clics seguidos se pueden acumular varios a la vez. */
.about-section__clap {
  position: absolute;
  font-family: var(--font-encode);
  font-weight: 700;
  font-size: 0.875rem;
  color: var(--darkpink);
  white-space: nowrap;
  pointer-events: none;
  animation: about-clap-pop 900ms ease-out forwards;
}

@keyframes about-clap-pop {
  0% {
    opacity: 0;
    transform: scale(0.4);
  }
  30% {
    opacity: 1;
    transform: scale(1.15);
  }
  45% {
    transform: scale(1);
  }
  100% {
    opacity: 0;
    transform: scale(0.9);
  }
}

.about-section__clap--pos-0 {
  top: -1.75rem;
  left: 0.25rem;
}

.about-section__clap--pos-1 {
  top: -1.5rem;
  left: 1.75rem;
}

.about-section__clap--pos-2 {
  top: 0.25rem;
  left: 2.25rem;
}

.about-section__clap--pos-3 {
  top: 1.75rem;
  left: 1.75rem;
}

.about-section__clap--pos-4 {
  top: 2rem;
  left: 0.25rem;
}

.about-section__clap--pos-5 {
  top: 1.75rem;
  left: -2rem;
}

.about-section__clap--pos-6 {
  top: 0.25rem;
  left: -2.5rem;
}

.about-section__clap--pos-7 {
  top: -1.5rem;
  left: -2rem;
}

.about-section__title {
  max-width: 24rem;
  line-height: 1.05;
}

.about-section__paragraph:first-of-type {
  margin-top: 0.5rem;
}

@media (min-width: 1200px) {
  .about-section {
    flex-direction: row;
    align-items: center;
    gap: 4rem;
    padding: 6rem 3rem;
  }

  .about-section__content {
    flex: 1 1 0;
    min-width: 0;
  }

  .about-section__title {
    max-width: 28rem;
  }

  .about-section__cards {
    flex: 1 1 0;
    min-width: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .about-section__content,
  .about-section__cards {
    transition: none;
    opacity: 1;
    transform: none;
  }

  .about-section__emoji {
    animation: none;
  }

  .about-section__clap {
    animation: none;
    opacity: 1;
  }
}
/* Orden de la secuencia: BootIntro -> NavDock -> PhotoCard a los 2.75s -> sus
   4 badges en cascada, terminan ~3.59s -> acá: el contenido de texto entra en
   cascada, uno detrás de otro, en el orden en que se lee: nombre, luego
   "Azano Rivers", luego la frase con líneas separadoras, luego la
   descripción, luego la cita final en cursiva, luego los 3 bloques de cifras.
   Después de esto (~4.95s) recién aparece LanguageSelector. Mobile: la foto
   queda arriba (mismo orden que la animación); desde 2xl se acomoda a la
   derecha con `row-reverse`. */
.banner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2rem;
  max-width: 90rem;
  margin: 0 auto;
  /* En mobile la card sube bien arriba (poco padding-top): no importa si
     queda casi debajo del selector de idioma, que es position:fixed y no
     compite por espacio en el flujo normal. Poco padding-bottom también: en
     mobile la siguiente sección (AboutSection) ya trae su propio padding-top,
     sumados quedaba un hueco enorme entre los botones y "Sobre Mí". */
  padding: 2.5rem 1.25rem 2rem;
}

.banner__content {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  max-width: 32rem;
  text-align: center;
}

.banner__name {
  opacity: 0;
  animation: banner-text-entrance 300ms ease-out 3600ms forwards;
}

.banner__handle {
  /* Achica el hueco solo entre título y subtítulo (el gap del contenedor es
     parejo para los 4 elementos; el line-height de .text-title/.text-subtitle
     ya deja aire de sobra entre ellos dos específicamente). */
  margin-top: -0.65rem;
  opacity: 0;
  animation: banner-text-entrance 300ms ease-out 3750ms forwards;
}

/* Sin cursiva: en vez de eso, unas líneas finas arriba y abajo la enmarcan
   para darle presencia, como una franja de datos rápidos. En rosa (no en el
   color de la grid): con el fondo cuadriculado, una línea del mismo tono que
   la grid prácticamente desaparece. */
.banner__tagline {
  margin-top: 0.75rem;
  padding: 0.5rem 0;
  border-top: 1px solid var(--darkpink);
  border-bottom: 1px solid var(--darkpink);
  font-weight: 600;
  opacity: 0;
  animation: banner-text-entrance 300ms ease-out 3900ms forwards;
}

.banner__tagline-highlight {
  color: var(--green);
}

.banner__description {
  margin-top: 0.5rem;
  opacity: 0;
  animation: banner-text-entrance 300ms ease-out 4050ms forwards;
}

.banner__quote {
  margin-top: 0.5rem;
  margin-bottom: 1.5rem;
  color: var(--darkpink);
  opacity: 0;
  animation: banner-text-entrance 300ms ease-out 4200ms forwards;
}

.banner__quote::before {
  content: '\201C';
}

.banner__quote::after {
  content: '\201D';
}

.banner__stats {
  display: flex;
  width: 100%;
  justify-content: center;
  gap: 0.75rem;
}

.banner__stats .stat-block {
  opacity: 0;
  animation: banner-text-entrance 300ms ease-out forwards;
}

.banner__stats .stat-block:nth-child(1) {
  animation-delay: 4350ms;
}

.banner__stats .stat-block:nth-child(2) {
  animation-delay: 4450ms;
}

.banner__stats .stat-block:nth-child(3) {
  animation-delay: 4550ms;
}

/* Cierra la cascada del banner: cada botón entra por separado, uno seguido
   del otro de izquierda a derecha (mismo cadenciado de 100ms que los stats),
   arrancando justo después del último stat. */
.banner__actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.75rem 1.25rem;
  margin-top: 1.5rem;
}

.banner__actions > * {
  opacity: 0;
  animation: banner-text-entrance 300ms ease-out forwards;
}

.banner__actions > *:nth-child(1) {
  animation-delay: 4650ms;
}

.banner__actions > *:nth-child(2) {
  animation-delay: 4750ms;
}

.banner__actions > *:nth-child(3) {
  animation-delay: 4850ms;
}

/* Aparece cuando termina toda la cascada del banner (los botones terminan a
   los 4850+300=5150ms; el <script> la muestra a los 5250ms) y desaparece en
   cuanto el usuario hace scroll. Usa `transition` (no `animation ...
   forwards`) para que la clase --hidden pueda apagarla sin pelear con el
   valor final de una animación. Es un <button> (easter egg: clic/toque hace
   que "explote", ver más abajo), no un ícono decorativo suelto. */
/* Solo en mobile: al lado del nombre, arriba a la derecha del bloque de
   contenido (que empieza justo con el <h1>). `position:absolute` a
   propósito: así queda totalmente afuera del flujo y nunca puede correr el
   título hacia la izquierda ni ocupar espacio propio, sin importar el ancho
   real que termine teniendo el botón. Vuelve a su lugar de siempre (abajo de
   los botones, en flujo normal) en desktop, ver media query 1200px. */
.banner__scroll-hint {
  position: absolute;
  top: 0.75rem;
  right: 1.75rem;
  width: 2.5rem;
  height: 2.5rem;
  padding: 0;
  border: none;
  background: none;
  cursor: help;
  opacity: 0;
  pointer-events: none;
  transition: opacity 400ms ease-out;
}

.banner__scroll-hint--visible {
  opacity: 1;
  pointer-events: auto;
  animation: banner-scroll-hint-bounce 1600ms ease-in-out 400ms infinite;
}

/* Ojo: opacity:0 sola no quita el clic, el botón sigue ahí. Sin esto se
   podía seguir haciendo clic (y ver el cursor de "?") en la zona aunque la
   flecha ya no se viera. */
.banner__scroll-hint--hidden {
  opacity: 0;
  pointer-events: none;
}

.banner__scroll-hint-icon {
  width: 100%;
  height: 100%;
  color: var(--darkpink);
}

/* Rebote (baja/sube) + pulso (agranda y brilla) en un solo keyframe: dos
   animations distintas no pueden pelear por la misma propiedad `transform`. */
@keyframes banner-scroll-hint-bounce {
  0%,
  100% {
    transform: translateY(0) scale(1);
    filter: drop-shadow(0 0 0 color-mix(in srgb, var(--darkpink) 0%, transparent));
  }
  50% {
    transform: translateY(0.5rem) scale(1.2);
    filter: drop-shadow(0 0 0.5rem color-mix(in srgb, var(--darkpink) 70%, transparent));
  }
}

/* Easter egg: al hacer clic/toque, "explota" (se detiene el rebote de arriba,
   el ícono estalla, sale una onda expansiva y el texto BOOM aparece
   estilo cómic), y recién cuando termina la más larga de las 3 animaciones
   (900ms), el <script> hace un empujoncito de scroll que la hace desaparecer
   de verdad. */
.banner__scroll-hint--exploding {
  animation: none;
}

.banner__scroll-hint--exploding .banner__scroll-hint-icon {
  animation: banner-boom-icon 600ms ease-out forwards;
}

.banner__scroll-hint--exploding::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 9999px;
  animation: banner-boom-burst 500ms ease-out forwards;
}

.banner__scroll-hint-boom {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) scale(0);
  opacity: 0;
  font-family: var(--font-encode);
  font-weight: 800;
  font-size: 1.5rem;
  letter-spacing: 0.02em;
  color: var(--darkpink);
  -webkit-text-stroke: 2px var(--white-90);
  white-space: nowrap;
  pointer-events: none;
}

.banner__scroll-hint--exploding .banner__scroll-hint-boom {
  animation: banner-boom-text 900ms ease-out forwards;
}

@keyframes banner-boom-icon {
  0% {
    transform: scale(1) rotate(0deg);
    opacity: 1;
  }
  30% {
    transform: scale(1.4) rotate(-15deg);
    opacity: 1;
  }
  100% {
    transform: scale(0.2) rotate(15deg);
    opacity: 0;
  }
}

@keyframes banner-boom-burst {
  0% {
    box-shadow: 0 0 0 0 color-mix(in srgb, var(--darkpink) 60%, transparent);
    opacity: 1;
  }
  100% {
    box-shadow: 0 0 0 2.5rem color-mix(in srgb, var(--darkpink) 0%, transparent);
    opacity: 0;
  }
}

@keyframes banner-boom-text {
  0%,
  20% {
    transform: translate(-50%, -50%) scale(0) rotate(-8deg);
    opacity: 0;
  }
  40% {
    transform: translate(-50%, -50%) scale(1.3) rotate(6deg);
    opacity: 1;
  }
  55% {
    transform: translate(-50%, -50%) scale(1) rotate(-4deg);
    opacity: 1;
  }
  80% {
    transform: translate(-50%, -50%) scale(1.1) rotate(2deg);
    opacity: 1;
  }
  100% {
    transform: translate(-50%, -50%) scale(0.5) rotate(0deg);
    opacity: 0;
  }
}

.banner__cta {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 1rem;
  border-radius: 9999px;
  font-family: var(--font-plex);
  font-size: 0.8rem;
  letter-spacing: 0.02em;
  text-decoration: none;
  transition: border-color 150ms ease, background-color 150ms ease, color 150ms ease;
}

.banner__cta--primary {
  border: 1px solid var(--darkpink);
  background-color: color-mix(in srgb, var(--darkpink) 15%, transparent);
  color: var(--white-90);
}

.banner__cta--primary:hover,
.banner__cta--primary:focus-visible {
  background-color: color-mix(in srgb, var(--darkpink) 30%, transparent);
  outline: none;
}

.banner__cta--secondary {
  border: 1px solid color-mix(in srgb, var(--lightblue) 50%, transparent);
  color: var(--white-90);
}

.banner__cta--secondary:hover,
.banner__cta--secondary:focus-visible {
  border-color: var(--lightblue);
  color: var(--lightblue);
  outline: none;
}

.banner__cta-icon {
  width: 0.9rem;
  height: 0.9rem;
  flex-shrink: 0;
}

/* Este no es un botón: solo texto con ícono, sin borde ni fondo. */
.banner__download {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  color: var(--white-70);
  font-family: var(--font-plex);
  font-size: 0.8rem;
  letter-spacing: 0.02em;
  text-decoration: none;
  transition: color 150ms ease;
}

.banner__download:hover,
.banner__download:focus-visible {
  color: var(--lightblue);
  outline: none;
}

/* Ancho fijo mínimo para que el botón no "salte" de tamaño al cambiar entre
   "Descargar CV" y "Espere...". */
.banner__download-label {
  display: inline-block;
  min-width: 5.5rem;
}

/* Mismo criterio que el "¡Clap!" de AboutSection: texto flotante que aparece
   con un pop y se desvanece, en una de 8 posiciones fijas precalculadas
   alrededor del botón (nada de inline styles/posiciones calculadas por JS). */
.banner__download-yay {
  position: absolute;
  font-family: var(--font-encode);
  font-weight: 700;
  font-size: 0.875rem;
  color: var(--green);
  white-space: nowrap;
  pointer-events: none;
  animation: banner-download-yay-pop 900ms ease-out forwards;
}

@keyframes banner-download-yay-pop {
  0% {
    opacity: 0;
    transform: scale(0.4);
  }
  30% {
    opacity: 1;
    transform: scale(1.15);
  }
  45% {
    transform: scale(1);
  }
  100% {
    opacity: 0;
    transform: scale(0.9);
  }
}

.banner__download-yay--pos-0 {
  top: -1.75rem;
  left: 0.25rem;
}

.banner__download-yay--pos-1 {
  top: -1.5rem;
  left: 3rem;
}

.banner__download-yay--pos-2 {
  top: 0;
  left: 6rem;
}

.banner__download-yay--pos-3 {
  top: 1.5rem;
  left: 5rem;
}

.banner__download-yay--pos-4 {
  top: 1.75rem;
  left: 2rem;
}

.banner__download-yay--pos-5 {
  top: 1.5rem;
  left: -1.5rem;
}

.banner__download-yay--pos-6 {
  top: 0;
  left: -2.5rem;
}

.banner__download-yay--pos-7 {
  top: -1.5rem;
  left: -1.5rem;
}

@keyframes banner-text-entrance {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@media (min-width: 1200px) {
  .banner {
    flex-direction: row-reverse;
    justify-content: center;
    align-items: center;
    gap: 4rem;
    padding: 8rem 3rem 4rem;
  }

  .banner__content {
    text-align: left;
    align-items: flex-start;
  }

  .banner__stats {
    justify-content: flex-start;
  }

  .banner__actions {
    justify-content: flex-start;
  }

  /* Desktop: vuelve al flujo normal, debajo de los botones (como antes de
     la excepción mobile de arriba). */
  .banner__scroll-hint {
    position: relative;
    top: auto;
    right: auto;
    align-self: center;
    margin-top: 0.5rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  .banner__name,
  .banner__handle,
  .banner__tagline,
  .banner__description,
  .banner__quote,
  .banner__stats .stat-block,
  .banner__actions > * {
    animation: none;
    opacity: 1;
  }

  .banner__scroll-hint {
    transition: none;
  }

  .banner__scroll-hint--visible {
    animation: none;
  }

  .banner__scroll-hint-icon,
  .banner__scroll-hint-boom,
  .banner__scroll-hint--exploding::before {
    animation: none;
  }

  .banner__download-yay {
    animation: none;
    opacity: 0;
  }

  .banner__scroll-hint--exploding .banner__scroll-hint-icon {
    opacity: 0;
  }
}
/* Migrado de HoloGallery (azanolabs-web). El modal (más abajo, `.cert-lightbox`)
   porta 1:1 el motor real del original: anillo virtual de tarjetas con
   imagen fija (nunca se reasigna `src`), posicionadas cada frame por una
   física de velocidad+fricción en `requestAnimationFrame` (ver el <script>
   del .astro). Ese mismo motor SÍ escribe `width`/`filter`/`z-index` por
   frame a propósito (igual que el original): con pocos elementos y sin
   pasar por React, ese costo es aceptable y es lo que da el movimiento
   fluido y orgánico real, no una aproximación con transiciones CSS. */
.certifications-section {
  max-width: 90rem;
  margin: 0 auto;
  padding: 2rem 1.25rem 4rem;
  opacity: 0;
  transition: opacity 600ms ease-out;
}

/* Skeleton de carga: compartido entre las tiles del strip y las 3 imágenes
   del modal. Va ARRIBA de la imagen (mayor z-index) y se desvanece cuando
   esa imagen termina de cargar (el <script> reactiva esto a mano en el modal,
   porque ahí se reasigna `src` sobre las mismas 3 imágenes en vez de crear
   elementos nuevos). El scroll del strip nunca espera por esto: es puramente
   visual, no bloquea nada. */
.cert-skeleton {
  position: absolute;
  inset: 0;
  z-index: 5;
  overflow: hidden;
  background:
    linear-gradient(color-mix(in srgb, var(--lightblue) 8%, transparent) 1px, transparent 1px),
    linear-gradient(90deg, color-mix(in srgb, var(--lightblue) 8%, transparent) 1px, transparent 1px),
    color-mix(in srgb, var(--background) 60%, black);
  background-size: 12px 12px, 12px 12px, auto;
  transition: opacity 400ms ease;
}

.cert-skeleton::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(
    90deg,
    transparent 0%,
    color-mix(in srgb, var(--lightblue) 15%, transparent) 30%,
    color-mix(in srgb, var(--white-90) 25%, transparent) 50%,
    color-mix(in srgb, var(--lightblue) 15%, transparent) 70%,
    transparent 100%
  );
  animation: cert-skeleton-sweep 1.9s ease-in-out infinite;
}

@keyframes cert-skeleton-sweep {
  from {
    transform: translateX(-170%);
  }
  to {
    transform: translateX(220%);
  }
}

.cert-skeleton--loaded {
  opacity: 0;
  pointer-events: none;
}

.certifications-section--visible {
  opacity: 1;
}

.certifications-section__header {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
  text-align: center;
  margin-bottom: 1rem;
}

.certifications-section__header-icon {
  width: 1.75rem;
  height: 1.75rem;
  margin-bottom: 0.5rem;
  color: var(--lightblue);
}

.certifications-section__subtitle {
  color: var(--white-70);
}

/* ————— Strip de miniaturas ————— */
.cert-strip {
  position: relative;
  margin-top: 2rem;
}

.cert-strip__scroll {
  overflow-x: auto;
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  cursor: grab;
}

.cert-strip__scroll::-webkit-scrollbar {
  display: none;
}

.cert-strip__scroll--dragging {
  cursor: grabbing;
  -webkit-user-select: none;
  user-select: none;
}

/* Niebla en los bordes (igual que el original de AzanoLabs: un degradé
   sólido del color de fondo hacia transparente, no una máscara) para que las
   miniaturas no se corten en seco contra el borde del strip. */
/* "Persianas": líneas horizontales repetidas sobre TODO el strip (no por
   miniatura), estilo scanline de CRT. Esto era lo que faltaba: no estaba en
   ninguna tile individual en el original, sino como una capa fija encima de
   todo el carrusel. */
.cert-strip__scanlines {
  pointer-events: none;
  position: absolute;
  inset: 0;
  z-index: 3;
  background: repeating-linear-gradient(
    0deg,
    transparent 0px,
    transparent 3px,
    color-mix(in srgb, var(--lightblue) 10%, transparent) 3px,
    color-mix(in srgb, var(--lightblue) 10%, transparent) 4px
  );
  mix-blend-mode: screen;
}

.cert-strip__edge {
  pointer-events: none;
  position: absolute;
  top: 0;
  bottom: 0;
  width: 5rem;
  z-index: 2;
}

.cert-strip__edge--left {
  left: 0;
  background: linear-gradient(to right, var(--background) 0%, transparent 100%);
}

.cert-strip__edge--right {
  right: 0;
  background: linear-gradient(to left, var(--background) 0%, transparent 100%);
}

.cert-strip__track {
  display: flex;
  gap: 1rem;
  width: max-content;
  padding: 0.5rem 1.25rem;
}

/* "Bisel" cyberpunk: esquina inferior derecha cortada en diagonal (clip-path,
   no border-radius), se acentúa un poco más en hover junto con un realce de
   brillo/saturación. */
/* `content-visibility:auto` + `contain-intrinsic-size`: con 84 certificados
   triplicados (252 tiles) para el loop infinito, cada uno tenía su propio
   `cert-tile-pulse` corriendo PARA SIEMPRE sin importar si estaba fuera de
   pantalla — 250+ animaciones infinitas activas todo el tiempo era el costo
   real detrás de "la tira se ve lenta". Esto le dice al navegador que se
   salte layout/paint/animación por completo en los tiles fuera de vista
   (o cerca de estarlo), sin cambiar nada del look de los que sí se ven.
   `contain-intrinsic-size` tiene que calzar EXACTO con el tamaño real (acá y
   en el breakpoint de 440px de más abajo): si no, el ancho reservado para un
   tile "saltado" no coincide con su tamaño real, y `scrollWidth` (del que
   depende el teletransporte del loop infinito) queda inestable. Progressive
   enhancement puro: en navegadores que no soportan la propiedad, se ignora
   y todo sigue exactamente como antes. */
.cert-tile {
  position: relative;
  flex-shrink: 0;
  width: 12rem;
  height: 7.5rem;
  padding: 0;
  border: none;
  overflow: hidden;
  background: none;
  cursor: pointer;
  content-visibility: auto;
  contain-intrinsic-size: 12rem 7.5rem;
  -webkit-clip-path: polygon(0 0, 100% 0, 100% calc(100% - 14px), calc(100% - 14px) 100%, 0 100%);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 14px), calc(100% - 14px) 100%, 0 100%);
  filter: brightness(0.85) saturate(0.9);
  transition: transform 280ms cubic-bezier(0.2, 0, 0, 1), filter 280ms ease, -webkit-clip-path 280ms ease, clip-path 280ms ease;
}

.cert-tile:hover,
.cert-tile:focus-visible {
  transform: scale(1.04);
  filter: brightness(1.05) saturate(1.15);
  -webkit-clip-path: polygon(0 0, 100% 0, 100% calc(100% - 20px), calc(100% - 20px) 100%, 0 100%);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 20px), calc(100% - 20px) 100%, 0 100%);
  outline: none;
}

/* Línea diagonal explícita en la esquina: el clip-path de arriba SOLO recorta
   la forma (no dibuja ningún borde sobre el corte), así que sin esto el
   "bisel" prácticamente no se nota. Va bien adentro del área visible (no
   pegada al borde recortado) para no arriesgarse a que el propio clip-path
   se la corte. */
.cert-tile__bevel-line {
  pointer-events: none;
  position: absolute;
  bottom: 9px;
  right: 5px;
  width: 26px;
  height: 2px;
  background: color-mix(in srgb, var(--lightblue) 70%, transparent);
  transform: rotate(-45deg);
  transform-origin: right center;
  transition: background-color 280ms ease, width 280ms ease;
}

.cert-tile:hover .cert-tile__bevel-line,
.cert-tile:focus-visible .cert-tile__bevel-line {
  width: 32px;
  background: var(--lightblue);
}

.cert-tile__image {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Borde holográfico: pulsa solo mientras la tile no tiene foco/hover
   (animation-play-state corre gratis en reposo, la pausamos en el glint de
   abajo en vez de acá porque este SÍ debe seguir latiendo siempre). Hereda
   el mismo bisel de arriba (clip-path: inherit) para que el borde también
   quede cortado en diagonal, no rectangular. */
.cert-tile__border {
  pointer-events: none;
  position: absolute;
  inset: 0;
  -webkit-clip-path: inherit;
  clip-path: inherit;
  border: 1px solid color-mix(in srgb, var(--lightblue) 20%, transparent);
  transition: border-color 280ms ease, box-shadow 280ms ease;
  animation: cert-tile-pulse 3.5s ease-in-out infinite;
}

.cert-tile:hover .cert-tile__border,
.cert-tile:focus-visible .cert-tile__border {
  border-color: color-mix(in srgb, var(--lightblue) 70%, transparent);
  animation: none;
  box-shadow: inset 0 0 12px color-mix(in srgb, var(--lightblue) 20%, transparent);
}

@keyframes cert-tile-pulse {
  0%,
  100% {
    border-color: color-mix(in srgb, var(--lightblue) 15%, transparent);
  }
  50% {
    border-color: color-mix(in srgb, var(--lightblue) 45%, transparent);
  }
}

/* Glint: banda diagonal completa (no una tira angosta) que barre la tile
   entera. El keyframe deja "estacionado" el brillo una parte del ciclo antes
   de repetirse (así no se ve como un barrido continuo sin pausa); ese hueco
   se achicó a propósito para que, si te quedás con el cursor encima, no
   tengas que esperar varios segundos a que vuelva a pasar. */
.cert-tile__glint {
  pointer-events: none;
  position: absolute;
  inset: 0;
  background: linear-gradient(
    135deg,
    transparent 20%,
    color-mix(in srgb, var(--lightblue) 10%, transparent) 50%,
    transparent 80%
  );
  transform: translateX(-120%);
  animation: cert-tile-glint 2.2s linear infinite;
  animation-play-state: paused;
}

.cert-tile:hover .cert-tile__glint,
.cert-tile:focus-visible .cert-tile__glint {
  animation-play-state: running;
}

@keyframes cert-tile-glint {
  0% {
    transform: translateX(-120%);
  }
  65%,
  100% {
    transform: translateX(120%);
  }
}

/* ————— Lightbox ————— */
/* Sin backdrop-filter: blur() a propósito (el original lo sacó por lo mismo):
   oscurecer con un color sólido bien opaco se ve prácticamente igual y no
   obliga a la GPU a capturar/desenfocar/recomponer TODO lo que hay detrás
   en cada frame mientras el modal está abierto. */
.cert-lightbox {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: none;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  background-color: color-mix(in srgb, var(--background) 80%, transparent);
  /* Aísla este subárbol del árbol de composición del resto de la página:
     sin esto, el navegador tiene que recalcular la composición de TODA la
     página en cada frame mientras el modal anima o el anillo 3D se mueve. */
  contain: layout style paint;
}

/* "Persianas" del fondo: mismo repeating-linear-gradient horizontal que el
   strip, pero acá cubriendo toda la pantalla del modal. */
.cert-lightbox__backdrop-scanlines {
  pointer-events: none;
  position: absolute;
  inset: 0;
  z-index: 0;
  background: repeating-linear-gradient(
    0deg,
    transparent 0px,
    transparent 3px,
    color-mix(in srgb, var(--lightblue) 6%, transparent) 3px,
    color-mix(in srgb, var(--lightblue) 6%, transparent) 4px
  );
}

.cert-lightbox__backdrop-grid {
  pointer-events: none;
  position: absolute;
  inset: 0;
  z-index: 0;
  background-image: linear-gradient(color-mix(in srgb, var(--lightblue) 5%, transparent) 1px, transparent 1px),
    linear-gradient(90deg, color-mix(in srgb, var(--lightblue) 5%, transparent) 1px, transparent 1px);
  background-size: 2.5rem 2.5rem;
  mask-image: radial-gradient(ellipse 80% 80% at 50% 50%, black 30%, transparent 100%);
}

/* Aberración cromática decorativa: estática, sin animación (el original la
   dejó así a propósito: animar scale/rotate en un elemento a pantalla
   completa cuesta presupuesto de repintado por un efecto casi imperceptible). */
.cert-lightbox__backdrop-chroma {
  pointer-events: none;
  position: absolute;
  inset: 0;
  z-index: 0;
  background: radial-gradient(
    ellipse 60% 60% at 50% 50%,
    transparent 40%,
    color-mix(in srgb, var(--lightblue) 8%, transparent) 60%,
    color-mix(in srgb, var(--darkpink) 6%, transparent) 80%,
    transparent 100%
  );
}

/* CRÍTICO: la versión anterior animaba `clip-path` sobre ESTE elemento
   (fullscreen, con las capas de scanlines+grid+chroma encima): eso obliga al
   navegador a repintar la pantalla entera en cada frame de la animación, casi
   bloqueando el hilo principal al abrir. Acá el overlay solo hace opacity
   (compositor puro); la sensación de "persiana" queda en el stage de abajo,
   que es chico y anima con transform, no con clip-path. */
.cert-lightbox--open {
  display: flex;
  animation: cert-lightbox-open 180ms ease-out both;
}

.cert-lightbox--closing {
  animation: cert-lightbox-close 160ms ease-in both;
  pointer-events: none;
}

@keyframes cert-lightbox-open {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@keyframes cert-lightbox-close {
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
  }
}

.cert-lightbox__close,
.cert-lightbox__nav {
  position: absolute;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  border-radius: 9999px;
  border: 1px solid var(--darkpink);
  background-color: color-mix(in srgb, var(--background) 85%, transparent);
  color: var(--white-90);
  cursor: pointer;
  transition: background-color 150ms ease;
}

.cert-lightbox__close:hover,
.cert-lightbox__nav:hover,
.cert-lightbox__close:focus-visible,
.cert-lightbox__nav:focus-visible {
  background-color: color-mix(in srgb, var(--darkpink) 30%, transparent);
  outline: none;
}

.cert-lightbox__close {
  top: 1rem;
  right: 1rem;
}

.cert-lightbox__nav {
  top: 50%;
  transform: translateY(-50%);
}

.cert-lightbox__nav--prev {
  left: 1rem;
}

.cert-lightbox__nav--next {
  right: 1rem;
}

/* Sin overflow:hidden: las tarjetas lejanas del anillo se salen bien afuera
   de este cuadro (llegan a asomar del otro lado de la pantalla), y como el
   modal entero (.cert-lightbox) sí tiene overflow:hidden, quedan contenidas
   ahí sin necesidad de recortar el stage mismo. `display:flex` + `center` es
   lo que le da a cada tarjeta (position:absolute, sin top/left propios) su
   posición de reposo centrada: el <script> solo le suma un `translateX` de
   ahí en más, igual que en el HoloLightbox original. */
.cert-lightbox__stage {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 72vh;
  /* Necesario para que el rotateY de las tarjetas vecinas se vea como
     profundidad real (perspectiva), no como un simple estirado plano. */
  perspective: 1200px;
  /* Reclama el gesto horizontal para nosotros: reduce la latencia con la
     que el navegador nos deja llamar preventDefault en touchmove. */
  touch-action: none;
  transform-origin: top center;
  /* El stage no intercepta clicks: cada tarjeta sí (pointer-events la pone
     el <script> por frame, según qué tan cerca está del centro). */
  pointer-events: none;
}

/* "Persiana" real, pero barata: acá el elemento es chico (no fullscreen como
   el overlay de arriba), y solo se anima `transform`/`opacity`, nunca
   `clip-path` — así el efecto de cortina bajando se ve igual de bien sin
   forzar repintado de pantalla completa. */
.cert-lightbox--open .cert-lightbox__stage {
  animation: cert-lightbox-stage-reveal 260ms cubic-bezier(0.16, 1, 0.3, 1) both;
}

@keyframes cert-lightbox-stage-reveal {
  from {
    transform: scaleY(0.05);
    opacity: 0;
  }
  to {
    transform: scaleY(1);
    opacity: 1;
  }
}

/* Cada tarjeta: SIN transición propia, a propósito — la posición/opacidad/
   filtro las escribe el <script> directo por frame (rAF con física de
   velocidad+fricción, portado 1:1 del HoloLightbox real de AzanoLabs). Una
   transición CSS acá pelearía contra esos valores y se vería con lag.
   `preserve-3d` para que el tilt de :hover de abajo (en cardInner) se
   renderice en el espacio 3D propio de la tarjeta. Sin top/left: se centra
   solo por el `justify-content:center` del stage (ver arriba), el <script>
   solo le suma `translateX` desde ahí. */
.cert-lightbox__card {
  position: absolute;
  background: none;
  border: none;
  padding: 0;
  outline: none;
  transform-style: preserve-3d;
  pointer-events: auto;
}

/* `width` SÍ lo escribe el <script> cada frame (interpolado junto con la
   posición): una transición CSS sobre `width` quedaría siempre ~100ms
   atrasada respecto al resto del movimiento, desalineando el borde de la
   imagen. Por eso acá la única transición es `transform`, y es SOLO para el
   tilt de :hover (que el <script> nunca toca). */
.cert-lightbox__card-inner {
  position: relative;
  overflow: hidden;
  -webkit-clip-path: polygon(0 0, 100% 0, 100% calc(100% - 16px), calc(100% - 16px) 100%, 0 100%);
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - 16px), calc(100% - 16px) 100%, 0 100%);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform 200ms ease;
}

/* Tilt al pasar el cursor sobre una tarjeta lateral: se inclina un poco MÁS
   hacia el espectador (compone con el rotateY que ya le puso el <script>).
   `--left`/`--right` los alterna el <script> según de qué lado del centro
   está cada tarjeta en cada frame (no son roles fijos, son continuos). */
.cert-lightbox__card--left:hover .cert-lightbox__card-inner {
  transform: rotateY(-22deg) translateZ(22px);
}

.cert-lightbox__card--right:hover .cert-lightbox__card-inner {
  transform: rotateY(22deg) translateZ(22px);
}

.cert-lightbox__image {
  display: block;
  width: 100%;
  height: auto;
  max-height: 66vh;
  object-fit: contain;
  -webkit-user-select: none;
  user-select: none;
}

.cert-lightbox__holo-border {
  pointer-events: none;
  position: absolute;
  inset: 0;
  z-index: 2;
  border: 1px solid color-mix(in srgb, var(--lightblue) 35%, transparent);
}

.cert-lightbox__card--active .cert-lightbox__holo-border {
  border-color: color-mix(in srgb, var(--lightblue) 60%, transparent);
  box-shadow: inset 0 0 20px color-mix(in srgb, var(--lightblue) 8%, transparent);
}

/* Mobile chico: sin espacio para mostrar los vecinos angulados, se oculta
   todo menos la tarjeta activa (la navegación sigue funcionando con
   swipe/rueda/flechas, solo cambia lo que se ve en pantalla). */
@media (max-width: 440px) {
  .cert-lightbox__card:not(.cert-lightbox__card--active) {
    display: none;
  }

  .cert-lightbox__card--active .cert-lightbox__card-inner {
    width: 92vw !important;
  }

  .cert-lightbox__stage {
    height: auto;
    min-height: 200px;
  }
}

/* ── Puntos indicadores (reemplaza el contador de texto) ─────────── */
.cert-lightbox__dots {
  position: absolute;
  bottom: 1.25rem;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: 0.5rem;
  z-index: 10;
}

.cert-lightbox__dot {
  width: 0.375rem;
  height: 0.375rem;
  border-radius: 9999px;
  background: color-mix(in srgb, var(--lightblue) 25%, transparent);
  transition: background-color 300ms ease, transform 300ms ease, box-shadow 300ms ease;
}

.cert-lightbox__dot--active {
  background: var(--lightblue);
  transform: scale(1.4);
  box-shadow: 0 0 6px color-mix(in srgb, var(--lightblue) 60%, transparent);
}

@media (min-width: 440px) {
  .cert-tile {
    width: 14rem;
    height: 8.75rem;
    contain-intrinsic-size: 14rem 8.75rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  .certifications-section {
    transition: none;
    opacity: 1;
  }

  .cert-tile__border,
  .cert-tile__glint {
    animation: none;
  }

  .cert-lightbox--open,
  .cert-lightbox--closing {
    animation: none;
  }

  .cert-lightbox--open .cert-lightbox__stage {
    animation: none;
  }

  .cert-lightbox__card-inner {
    transition: none;
  }

  .cert-skeleton::after {
    animation: none;
  }

  .cert-skeleton {
    transition: none;
  }
}
.contact-section {
  max-width: 40rem;
  margin: 0 auto;
  padding: 2rem 1.25rem 4rem;
  opacity: 0;
  transition: opacity 600ms ease-out;
}

.contact-section--visible {
  opacity: 1;
}

.contact-section__header {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
  text-align: center;
  margin-bottom: 2rem;
}

.contact-section__header-icon {
  width: 1.75rem;
  height: 1.75rem;
  margin-bottom: 0.5rem;
  color: var(--lightblue);
}

.contact-section__subtitle {
  color: var(--white-70);
}

.contact-section__socials {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
}

.contact-section__email {
  margin-top: 2rem;
}

.contact-section__social {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 3rem;
  height: 3rem;
  border-radius: 9999px;
  border: 1px solid color-mix(in srgb, var(--lightblue) 50%, transparent);
  color: var(--white-70);
  transition: border-color 150ms ease, color 150ms ease, background-color 150ms ease, transform 150ms ease;
}

.contact-section__social:hover,
.contact-section__social:focus-visible {
  border-color: var(--darkpink);
  background-color: color-mix(in srgb, var(--darkpink) 15%, transparent);
  color: var(--darkpink);
  transform: translateY(-0.15rem);
  outline: none;
}

.contact-section__social-icon {
  width: 1.375rem;
  height: 1.375rem;
}

@media (min-width: 440px) {
  .contact-section__social {
    width: 3.5rem;
    height: 3.5rem;
  }

  .contact-section__social-icon {
    width: 1.625rem;
    height: 1.625rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  .contact-section {
    transition: none;
    opacity: 1;
  }

  .contact-section__social {
    transition: none;
  }

  .contact-section__social:hover,
  .contact-section__social:focus-visible {
    transform: none;
  }
}
.education-section {
  max-width: 72rem;
  margin: 0 auto;
  padding: 2rem 1.25rem 4rem;
  opacity: 0;
  transition: opacity 600ms ease-out;
}

.education-section--visible {
  opacity: 1;
}

.education-section__header {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
  text-align: center;
  margin-bottom: 2rem;
}

.education-section__header-icon {
  width: 1.75rem;
  height: 1.75rem;
  margin-bottom: 0.5rem;
  color: var(--lightblue);
}

/* Sin subtítulo acá: en su lugar una descripción corta de una línea, un poco
   más discreta que un párrafo de cuerpo normal. */
.education-section__description {
  max-width: 28rem;
  color: var(--white-70);
}

.education-section__cards {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
}

@media (min-width: 600px) {
  .education-section__cards {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 1200px) {
  .education-section {
    padding: 2rem 3rem 6rem;
  }

  .education-section__cards {
    grid-template-columns: repeat(4, 1fr);
  }
}

@media (prefers-reduced-motion: reduce) {
  .education-section {
    transition: none;
    opacity: 1;
  }
}
/* Línea de tiempo horizontal 100% CSS + scroll nativo del navegador: nada de
   canvas/WebGPU ni JS en el hot-path del scroll. El scroll horizontal nativo
   ya corre en el hilo de composición (GPU), es más fluido y más accesible
   que reimplementarlo a mano. Las posiciones de cada tarjeta están
   precalculadas (año de inicio/fin * ancho-por-año) y escritas como CSS
   estático por ítem, mismo patrón que FloatingLeaves: nada de style={}.
   Orquestación completa (ver .astro): 1) clic en el gate -> el texto se
   desvanece -> 2) el botón se estira hasta el tamaño de la caja de la línea
   de tiempo (mismo look: borde/fondo rosa, "se agranda el botón") -> 3) se
   intercambia por el contenido real y arrancan, en cascada, las 6 tarjetas
   -> 4) la regla/líneas conectoras -> 5) la barra de progreso parpadea para
   llamar la atención, igual que el ícono de LanguageSelector. */
.experience-section {
  max-width: 90rem;
  margin: 0 auto;
  padding: 2rem 1.25rem 4rem;
  opacity: 0;
  transition: opacity 600ms ease-out;
}

.experience-section--visible {
  opacity: 1;
}

.experience-section__header {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
  text-align: center;
  margin-bottom: 2rem;
}

/* Solo para que el hueco de arriba no se vea tan vacío entre el final de la
   sección anterior y el título: chico a propósito, es un acento, no un ícono
   protagonista. */
.experience-section__header-icon {
  width: 1.75rem;
  height: 1.75rem;
  margin-bottom: 0.5rem;
  color: var(--darkpink);
}

.experience-section__subtitle {
  color: var(--white-70);
}

/* Gate: mismo look que la caja de la línea de tiempo (borde + fondo rosa
   translúcido), para que estirarse hasta esa caja se sienta continuo. Altura
   y max-width concretas (no `auto`/`none`): sin un valor real de partida y
   de llegada, la transición no puede interpolar y salta de golpe. */
.experience-section__gate {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  max-width: 24rem;
  height: 4.5rem;
  margin: 0 auto;
  padding: 0 1.5rem;
  border-radius: 0.75rem;
  border: 1px solid var(--darkpink);
  background-color: color-mix(in srgb, var(--darkpink) 12%, transparent);
  color: var(--white-90);
  cursor: pointer;
  overflow: hidden;
  transition: max-width 550ms ease, height 550ms ease, background-color 150ms ease;
  animation: experience-gate-pulse 2200ms ease-in-out infinite;
}

.experience-section__gate:hover,
.experience-section__gate:focus-visible {
  background-color: color-mix(in srgb, var(--darkpink) 25%, transparent);
  outline: none;
}

/* Pulso en el borde para llamar la atención mientras está cerrado: se
   detiene apenas se hace clic (las clases de abajo lo apagan). */
@keyframes experience-gate-pulse {
  0%,
  100% {
    box-shadow: 0 0 0 0 color-mix(in srgb, var(--darkpink) 45%, transparent);
  }
  50% {
    box-shadow: 0 0 0 0.5rem color-mix(in srgb, var(--darkpink) 0%, transparent);
  }
}

.experience-section__gate--collapsing,
.experience-section__gate--expanding {
  animation: none;
}

.experience-section__gate-label {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  transition: opacity 200ms ease;
}

.experience-section__gate--collapsing .experience-section__gate-label {
  opacity: 0;
}

.experience-section__gate-title {
  font-family: var(--font-encode);
  font-weight: 700;
  font-size: 0.9375rem;
  line-height: 1.3;
  text-align: center;
}

.experience-section__gate-subtitle {
  font-family: var(--font-plex);
  font-size: 0.75rem;
  color: var(--white-70);
  text-align: center;
}

/* Altura estimada para calzar con la caja real de la línea de tiempo
   (padding + barra de progreso + alto fijo del scroll interno). Puede
   necesitar un ajuste fino luego de revisarlo en el navegador. */
.experience-section__gate--expanding {
  max-width: 100%;
  height: 30rem;
}

.experience-section--revealed .experience-section__gate {
  display: none;
}

/* Caja contenedora: mismo borde/fondo rosa que el gate (es "el botón
   agrandado"), para que se entienda como un bloque aparte. */
.experience-timeline {
  position: relative;
  display: none;
  flex-direction: column;
  gap: 1rem;
  margin-top: 2rem;
  padding: 1.25rem;
  border-radius: 1rem;
  border: 1px solid var(--darkpink);
  background-color: color-mix(in srgb, var(--darkpink) 12%, transparent);
}

/* Cerrar: arriba a la izquierda, vuelve a colapsar todo al estado de botón. */
.experience-timeline__close {
  position: absolute;
  top: 0.75rem;
  left: 0.75rem;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border-radius: 9999px;
  border: 1px solid var(--darkpink);
  background-color: color-mix(in srgb, var(--background) 85%, transparent);
  color: var(--white-90);
  cursor: pointer;
  transition: background-color 150ms ease, color 150ms ease;
}

.experience-timeline__close:hover,
.experience-timeline__close:focus-visible {
  background-color: color-mix(in srgb, var(--darkpink) 30%, transparent);
  outline: none;
}

.experience-timeline__close-icon {
  width: 1.1rem;
  height: 1.1rem;
}

.experience-section--revealed .experience-timeline {
  display: flex;
}

/* Barra de progreso: chica y a la derecha (simétrica al botón de cerrar), no
   centrada arriba: ahí se confundía con el dock, que en desktop también vive
   centrado y arriba. Al terminar de aparecer todo, el número parpadea en
   rosa para llamar la atención (mismo keyframe/lógica que el ícono de
   LanguageSelector). */
.experience-timeline__progress-wrap {
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.experience-timeline__progress {
  width: 3rem;
  height: 4px;
  appearance: none;
  border: none;
  border-radius: 9999px;
  overflow: hidden;
  background-color: color-mix(in srgb, var(--lightblue) 15%, transparent);
}

.experience-timeline__progress::-webkit-progress-bar {
  background-color: color-mix(in srgb, var(--lightblue) 15%, transparent);
  border-radius: 9999px;
}

.experience-timeline__progress::-webkit-progress-value {
  background-color: var(--darkpink);
  border-radius: 9999px;
  transition: width 80ms linear;
}

.experience-timeline__progress::-moz-progress-bar {
  background-color: var(--darkpink);
  border-radius: 9999px;
  transition: width 80ms linear;
}

.experience-timeline__progress-label {
  font-family: var(--font-plex);
  font-weight: 600;
  font-size: 0.75rem;
  color: var(--white-70);
}

@media (min-width: 1200px) {
  .experience-timeline__progress-wrap {
    top: 1rem;
    right: 1rem;
    gap: 0.75rem;
  }

  .experience-timeline__progress {
    width: 6rem;
    height: 6px;
  }

  .experience-timeline__progress-label {
    font-size: 1rem;
  }
}

.experience-timeline--revealing .experience-timeline__progress-label {
  animation: experience-progress-attention 1200ms ease-in-out 1700ms 1;
}

@keyframes experience-progress-attention {
  0%,
  33%,
  66%,
  100% {
    color: var(--white-90);
  }
  16.5%,
  50%,
  83% {
    color: var(--darkpink);
  }
}

/* Ícono de arrastre: arriba, centrado horizontal (no en la mitad de la caja),
   avisa que se puede mover para los 4 lados. Se queda oculto hasta que el
   <script> le agrega `--ready` (recién cuando termina TODA la cascada de
   tarjetas + líneas, 1700ms): antes dependía de un animation-delay de CSS
   mientras el elemento seguía en opacity:0 por transition, y en la práctica
   el navegador lo dejaba visible sin que el vaivén arrancara. Con `--ready`
   agregado por JS en el momento justo, no hace falta ningún delay: arranca
   apenas se le agrega la clase. En touch (`pointer: coarse`) se cambia la
   mano por un dedo. */
.experience-timeline__hint {
  position: absolute;
  top: 0.75rem;
  left: 50%;
  z-index: 1;
  opacity: 0;
  pointer-events: none;
  transition: opacity 400ms ease-out;
  transform: translateX(-50%);
}

.experience-timeline__hint--ready {
  opacity: 1;
  animation: experience-hint-wiggle 2400ms ease-in-out infinite;
}

.experience-timeline__hint--hidden {
  opacity: 0;
}

/* Más recorrido (0.4rem -> 0.7rem) y con un pulso de escala metido adentro:
   el movimiento chico anterior era casi imperceptible sobre un ícono tan
   chico. */
@keyframes experience-hint-wiggle {
  0%,
  100% {
    transform: translateX(-50%) translate(0, 0) scale(1);
  }
  25% {
    transform: translateX(-50%) translate(0.7rem, 0) scale(1.15);
  }
  50% {
    transform: translateX(-50%) translate(0, 0.7rem) scale(1.15);
  }
  75% {
    transform: translateX(-50%) translate(-0.7rem, 0) scale(1.15);
  }
}

.experience-timeline__hint-icon {
  width: 2rem;
  height: 2rem;
  color: var(--white-70);
}

.experience-timeline__hint-icon--mobile {
  display: none;
}

@media (pointer: coarse) {
  .experience-timeline__hint-icon--desktop {
    display: none;
  }

  .experience-timeline__hint-icon--mobile {
    display: block;
  }
}

/* Scroll nativo, pero sin la barra visible (con la barra de progreso propia
   ya alcanza; la nativa del navegador se veía fuera de lugar acá). Cursor de
   "manito" para dar a entender que se puede arrastrar con el mouse. Alto fijo
   y menor que el track real: la fila de Freelance queda debajo, se llega
   arrastrando/scrolleando verticalmente en vez de agrandar toda la caja.
   Máscara en los 4 bordes: en vez de cortar las tarjetas en seco contra el
   padding, se desvanecen. En Safari (sin mask-composite) queda solo el
   desvanecido horizontal, que es el que más se nota. */
.experience-timeline__scroll {
  position: relative;
  max-height: 24rem;
  overflow-x: auto;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  cursor: grab;
  -webkit-mask-image: linear-gradient(
    to right,
    transparent 0,
    black 2rem,
    black calc(100% - 2rem),
    transparent 100%
  );
  mask-image:
    linear-gradient(to right, transparent 0, black 2rem, black calc(100% - 2rem), transparent 100%),
    linear-gradient(to bottom, transparent 0, black 1.5rem, black calc(100% - 1.5rem), transparent 100%);
  mask-composite: intersect;
}

.experience-timeline__scroll::-webkit-scrollbar {
  display: none;
}

.experience-timeline__scroll--dragging {
  cursor: grabbing;
  -webkit-user-select: none;
  user-select: none;
}

.experience-timeline__track {
  position: relative;
  width: 156rem;
  min-height: 46rem;
  /* Padding izquierdo más grande que el resto: la tarjeta de Ximil (ítem
     angosto) es más ancha que su propio ítem y se sale un poco hacia la
     izquierda; sin este margen quedaba dentro de la zona de desvanecido de
     la máscara del scroll y se veía "cortada". */
  padding: 2rem 4rem 2rem 3.5rem;
}

.experience-timeline__ruler {
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  height: 2px;
  background-color: color-mix(in srgb, var(--lightblue) 40%, transparent);
}

/* Segundo carril, paralelo, para Freelance: nace del carril principal con
   una bifurcación estilo GitHub (el <svg> de abajo), no es solo una barra
   sobre la misma línea. */
.experience-timeline__freelance-line {
  position: absolute;
  top: calc(50% + 0.5rem);
  left: 88rem;
  right: 0;
  height: 2px;
  background-color: color-mix(in srgb, var(--darkpink) 40%, transparent);
}

.experience-timeline__branch {
  position: absolute;
  top: 50%;
  left: 86rem;
  width: 2rem;
  height: 0.5rem;
  overflow: visible;
}

.experience-timeline__branch path {
  stroke: color-mix(in srgb, var(--darkpink) 40%, transparent);
}

.experience-timeline__item {
  position: absolute;
  top: 0;
  height: 100%;
}

.experience-timeline__item--main {
  --node-color: var(--lightblue);
}

.experience-timeline__item--freelance {
  --node-color: var(--darkpink);
}

/* La tarjeta tiene un ancho fijo y legible (no el ancho del rango de fechas:
   eso dejaba tarjetas gigantes casi vacías para los trabajos más largos).
   Va centrada sobre el punto medio de su propio rango, encima/debajo de su
   carril según la fila. Offsets con harto margen: alguna tarjeta con texto
   más largo (ej. nombres de empresa largos) no debe quedar sin espacio. */
.experience-timeline__item--main .experience-card {
  position: absolute;
  bottom: calc(50% + 2rem);
  left: 50%;
  width: 17rem;
  transform: translateX(-50%);
}

.experience-timeline__item--freelance .experience-card {
  position: absolute;
  top: calc(50% + 2.5rem);
  left: 50%;
  width: 17rem;
  transform: translateX(-50%);
}

/* Barra sobre el carril: representa la duración real (va del inicio al fin
   exacto del rango, todo el ancho del ítem), independiente del tamaño fijo
   de la tarjeta. La principal va sobre la regla de arriba; la de Freelance
   va sobre su propio carril de abajo. */
.experience-timeline__item--main .experience-timeline__bar {
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  height: 4px;
  background-color: var(--node-color);
  transform: translateY(-50%);
  border-radius: 9999px;
}

.experience-timeline__item--freelance .experience-timeline__bar {
  position: absolute;
  top: calc(50% + 0.5rem);
  left: 0;
  right: 0;
  height: 4px;
  background-image: linear-gradient(90deg, var(--node-color) 0%, var(--node-color) 85%, transparent 100%);
  border-radius: 9999px;
}

.experience-timeline__item--codelabs .experience-timeline__bar {
  background-image: linear-gradient(90deg, var(--node-color) 0%, var(--node-color) 85%, transparent 100%);
  background-color: transparent;
}

/* Conector: una línea corta desde el borde de la tarjeta hasta su carril, más
   un punto en el centro del rango de fechas (mismo eje que la tarjeta). */
.experience-timeline__item--main::before {
  content: '';
  position: absolute;
  left: 50%;
  bottom: 50%;
  width: 1px;
  height: 2rem;
  background-color: var(--node-color);
  transform: translateX(-50%);
}

.experience-timeline__item--freelance::before {
  content: '';
  position: absolute;
  left: 50%;
  top: calc(50% + 0.5rem);
  width: 1px;
  height: 2rem;
  background-color: var(--node-color);
  transform: translateX(-50%);
}

.experience-timeline__item--main::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 0.65rem;
  height: 0.65rem;
  border-radius: 9999px;
  background-color: var(--node-color);
  box-shadow: 0 0 0 3px var(--background);
  transform: translate(-50%, -50%);
}

.experience-timeline__item--freelance::after {
  content: '';
  position: absolute;
  top: calc(50% + 0.5rem);
  left: 50%;
  width: 0.65rem;
  height: 0.65rem;
  border-radius: 9999px;
  background-color: var(--node-color);
  box-shadow: 0 0 0 3px var(--background);
  transform: translate(-50%, -50%);
}

/* Posiciones con ritmo uniforme: el CENTRO de cada ítem está separado 22rem
   del siguiente (no proporcional a los años reales, eso dejaba saltos como
   Ximil->Avancys mucho más separados que el resto). Como la tarjeta visible
   siempre mide 17rem sin importar el ancho real del ítem/barra debajo, un
   centro-a-centro uniforme automáticamente da un espacio uniforme de 5rem
   (22 - 17) entre el borde de cada tarjeta y la siguiente. El ancho de cada
   ítem (y su barra sobre la regla) sí sigue siendo real (años * 7rem/año);
   solo cambia dónde queda el CENTRO de ese ancho real. */
.experience-timeline__item--ximil {
  left: 8rem;
  width: 7rem;
}

.experience-timeline__item--avancys {
  left: 30rem;
  width: 7rem;
}

.experience-timeline__item--sic {
  left: 52rem;
  width: 7rem;
}

.experience-timeline__item--bbva {
  left: 67rem;
  width: 21rem;
}

.experience-timeline__item--perspectivaK {
  left: 96rem;
  width: 7rem;
}

.experience-timeline__item--codelabs {
  left: 111rem;
  width: 21rem;
}

/* Arranca justo donde termina BBVA (88rem = 67+21): el freelance empieza el
   mismo año en que termina ese trabajo formal, y de ahí sale la bifurcación
   hacia su propio carril. */
.experience-timeline__item--freelance {
  left: 88rem;
  width: 57rem;
}

/* Revelación en cascada: primero las 6 tarjetas, una por una, y solo cuando
   termina la última aparece la regla/líneas conectoras. Todo gateado detrás
   de una sola clase (agregada por JS tras el estirón del botón), igual que
   el resto de las secciones de este sitio. */
.experience-card,
.experience-timeline__bar,
.experience-timeline__item::before,
.experience-timeline__item::after {
  opacity: 0;
}

.experience-timeline__ruler,
.experience-timeline__freelance-line,
.experience-timeline__branch {
  opacity: 0;
}

.experience-timeline--revealing .experience-card {
  animation: experience-fade-in 300ms ease-out forwards;
}

.experience-timeline--revealing .experience-timeline__item--ximil .experience-card {
  animation-delay: 0ms;
}

.experience-timeline--revealing .experience-timeline__item--avancys .experience-card {
  animation-delay: 200ms;
}

.experience-timeline--revealing .experience-timeline__item--sic .experience-card {
  animation-delay: 400ms;
}

.experience-timeline--revealing .experience-timeline__item--bbva .experience-card {
  animation-delay: 600ms;
}

.experience-timeline--revealing .experience-timeline__item--perspectivaK .experience-card {
  animation-delay: 800ms;
}

.experience-timeline--revealing .experience-timeline__item--codelabs .experience-card {
  animation-delay: 1000ms;
}

.experience-timeline--revealing .experience-timeline__item--freelance .experience-card {
  animation-delay: 1200ms;
}

.experience-timeline--revealing .experience-timeline__ruler,
.experience-timeline--revealing .experience-timeline__freelance-line,
.experience-timeline--revealing .experience-timeline__branch,
.experience-timeline--revealing .experience-timeline__bar,
.experience-timeline--revealing .experience-timeline__item::before,
.experience-timeline--revealing .experience-timeline__item::after {
  animation: experience-fade-in 400ms ease-out 1500ms forwards;
}

@keyframes experience-fade-in {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

@media (prefers-reduced-motion: reduce) {
  .experience-section {
    transition: none;
    opacity: 1;
  }

  .experience-section__gate-label,
  .experience-section__gate {
    transition: none;
  }

  .experience-section__gate {
    animation: none;
  }

  .experience-card,
  .experience-timeline__bar,
  .experience-timeline__item::before,
  .experience-timeline__item::after,
  .experience-timeline__ruler,
  .experience-timeline__freelance-line,
  .experience-timeline__branch {
    animation: none;
    opacity: 1;
  }

  .experience-timeline__progress-label {
    animation: none;
  }

  .experience-timeline__hint {
    transition: none;
    animation: none;
    opacity: 1;
  }
}
/* Migrado de CreatedBySignature + NeonTextGlitch (azanolabs-web): en el
   original es la única pieza del footer, un link centrado con la palabra
   "AzanoRivers" en glitch neon (aberración cromática vía pseudo-elementos
   que duplican el texto con `attr()`, cada uno corrido y con su propio
   `text-shadow` de color, animados con `clip-path` en bandas). Portado acá
   1:1 en mecánica, con los tokens de color de este proyecto en vez de los
   `--neon-*` originales. */
/* El padding-bottom extra es a propósito: en mobile/tablet el NavDock vive
   fijo abajo (bottom: 1rem, ver NavDock.css) y flota ENCIMA del final de la
   página, tapando el footer si no le dejamos ese espacio libre debajo. Recién
   en desktop (min-width: 1200px) el dock se muda arriba, así que ahí vuelve
   al padding normal. */
.site-footer {
  display: flex;
  justify-content: center;
  padding: 2rem 1.25rem 5rem;
  border-top: 1px solid color-mix(in srgb, var(--lightblue) 15%, transparent);
  opacity: 0;
  transition: opacity 600ms ease-out;
}

.site-footer--visible {
  opacity: 1;
}

@media (min-width: 1200px) {
  .site-footer {
    padding-bottom: 2.5rem;
  }
}

.site-footer__signature {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.4em;
  font-family: var(--font-plex);
  font-size: 0.875rem;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--white-70);
  transition: color 200ms ease;
}

@media (min-width: 360px) {
  .site-footer__signature {
    font-size: 0.9375rem;
  }
}

@media (min-width: 440px) {
  .site-footer__signature {
    font-size: 1.0625rem;
  }
}

@media (min-width: 600px) {
  .site-footer__signature {
    font-size: 1.1875rem;
  }
}

@media (min-width: 1200px) {
  .site-footer__signature {
    font-size: 1.5rem;
    letter-spacing: 0.25em;
  }
}

.site-footer__signature:hover,
.site-footer__signature:focus-visible {
  color: var(--white-90);
  outline: none;
}

/* Base del glitch: `isolation:isolate` + `contain` para que las bandas
   animadas de los pseudo-elementos (más abajo) no obliguen a repintar nada
   fuera de este span. */
.site-footer__glitch {
  position: relative;
  display: inline-block;
  font-weight: 700;
  color: var(--white-90);
  isolation: isolate;
  contain: layout style paint;
  filter: drop-shadow(0 0 6px color-mix(in srgb, var(--lightblue) 90%, transparent));
}

.site-footer__glitch::before,
.site-footer__glitch::after {
  content: attr(data-glitch-text);
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  -webkit-clip-path: inset(0 0 0 0);
  clip-path: inset(0 0 0 0);
  z-index: -1;
  will-change: transform, clip-path;
}

.site-footer__glitch::before {
  left: 2px;
  text-shadow: -2px 0 var(--darkpink);
  animation: site-footer-glitch-shift 6s infinite;
}

.site-footer__glitch::after {
  left: -2px;
  text-shadow: 2px 0 var(--lightblue);
  animation: site-footer-glitch-shift-alt 5s infinite;
}

/* En mobile/tablet: sin clip-path ni transform (repintaría por frame). Solo
   un parpadeo de opacidad en los pseudo-elementos, 100% compositor — el
   `text-shadow` corrido ya da la aberración cromática sin mover nada. */
@media (max-width: 1199px) {
  .site-footer__glitch::before {
    -webkit-clip-path: none;
    clip-path: none;
    transform: none;
    animation: site-footer-glitch-mobile-a 4s infinite;
    will-change: opacity;
  }

  .site-footer__glitch::after {
    -webkit-clip-path: none;
    clip-path: none;
    transform: none;
    animation: site-footer-glitch-mobile-b 3.2s infinite;
    will-change: opacity;
  }
}

@keyframes site-footer-glitch-mobile-a {
  0%,
  55% {
    opacity: 0;
  }
  56% {
    opacity: 0.7;
  }
  57% {
    opacity: 0;
  }
  72% {
    opacity: 0;
  }
  73% {
    opacity: 0.5;
  }
  74% {
    opacity: 0;
  }
  100% {
    opacity: 0;
  }
}

@keyframes site-footer-glitch-mobile-b {
  0%,
  30% {
    opacity: 0;
  }
  31% {
    opacity: 0.6;
  }
  32% {
    opacity: 0;
  }
  33% {
    opacity: 0.4;
  }
  34% {
    opacity: 0;
  }
  100% {
    opacity: 0;
  }
}

/* Nota iOS Safari: `-webkit-clip-path` en cada paso, no solo en la regla
   base — Safari < 16 puede ignorar el `clip-path` sin prefijo dentro de un
   @keyframes aunque sí soporte la propiedad estática prefijada. Solo corre
   en desktop real (ver el override de arriba para <1200px), pero un iPad
   apaisado sí puede caer en este breakpoint siendo WebKit/Safari. */
@keyframes site-footer-glitch-shift {
  0% {
    -webkit-clip-path: inset(42% 0 56% 0);
    clip-path: inset(42% 0 56% 0);
    transform: translate3d(0, 0, 0) skew(0.3deg);
  }
  10% {
    -webkit-clip-path: inset(12% 0 41% 0);
    clip-path: inset(12% 0 41% 0);
    transform: translate3d(-1px, 0, 0) skew(0.5deg);
  }
  20% {
    -webkit-clip-path: inset(63% 0 37% 0);
    clip-path: inset(63% 0 37% 0);
    transform: translate3d(1px, 0, 0) skew(0.4deg);
  }
  30% {
    -webkit-clip-path: inset(86% 0 14% 0);
    clip-path: inset(86% 0 14% 0);
    transform: translate3d(0, 0, 0) skew(0.3deg);
  }
  40% {
    -webkit-clip-path: inset(26% 0 40% 0);
    clip-path: inset(26% 0 40% 0);
    transform: translate3d(-1px, 0, 0) skew(0.2deg);
  }
  50% {
    -webkit-clip-path: inset(76% 0 24% 0);
    clip-path: inset(76% 0 24% 0);
    transform: translate3d(1px, 0, 0) skew(0.1deg);
  }
  60% {
    -webkit-clip-path: inset(68% 0 32% 0);
    clip-path: inset(68% 0 32% 0);
    transform: translate3d(0, 0, 0) skew(0.5deg);
  }
  70% {
    -webkit-clip-path: inset(82% 0 18% 0);
    clip-path: inset(82% 0 18% 0);
    transform: translate3d(-1px, 0, 0) skew(0.4deg);
  }
  80% {
    -webkit-clip-path: inset(54% 0 40% 0);
    clip-path: inset(54% 0 40% 0);
    transform: translate3d(1px, 0, 0) skew(0.3deg);
  }
  90% {
    -webkit-clip-path: inset(60% 0 15% 0);
    clip-path: inset(60% 0 15% 0);
    transform: translate3d(0, 0, 0) skew(0.2deg);
  }
  100% {
    -webkit-clip-path: inset(67% 0 33% 0);
    clip-path: inset(67% 0 33% 0);
    transform: translate3d(0, 0, 0) skew(0.1deg);
  }
}

@keyframes site-footer-glitch-shift-alt {
  0% {
    -webkit-clip-path: inset(65% 0 0 0);
    clip-path: inset(65% 0 0 0);
    transform: translate3d(0, 0, 0) skew(0.4deg);
  }
  10% {
    -webkit-clip-path: inset(79% 0 21% 0);
    clip-path: inset(79% 0 21% 0);
    transform: translate3d(1px, 0, 0) skew(0.6deg);
  }
  20% {
    -webkit-clip-path: inset(90% 0 10% 0);
    clip-path: inset(90% 0 10% 0);
    transform: translate3d(-1px, 0, 0) skew(0.5deg);
  }
  30% {
    -webkit-clip-path: inset(71% 0 0 0);
    clip-path: inset(71% 0 0 0);
    transform: translate3d(0, 0, 0) skew(0.4deg);
  }
  40% {
    -webkit-clip-path: inset(56% 0 23% 0);
    clip-path: inset(56% 0 23% 0);
    transform: translate3d(1px, 0, 0) skew(0.3deg);
  }
  50% {
    -webkit-clip-path: inset(36% 0 6% 0);
    clip-path: inset(36% 0 6% 0);
    transform: translate3d(-1px, 0, 0) skew(0.2deg);
  }
  60% {
    -webkit-clip-path: inset(18% 0 28% 0);
    clip-path: inset(18% 0 28% 0);
    transform: translate3d(0, 0, 0) skew(0.6deg);
  }
  70% {
    -webkit-clip-path: inset(45% 0 14% 0);
    clip-path: inset(45% 0 14% 0);
    transform: translate3d(1px, 0, 0) skew(0.5deg);
  }
  80% {
    -webkit-clip-path: inset(33% 0 31% 0);
    clip-path: inset(33% 0 31% 0);
    transform: translate3d(-1px, 0, 0) skew(0.4deg);
  }
  90% {
    -webkit-clip-path: inset(21% 0 56% 0);
    clip-path: inset(21% 0 56% 0);
    transform: translate3d(0, 0, 0) skew(0.3deg);
  }
  100% {
    -webkit-clip-path: inset(84% 0 16% 0);
    clip-path: inset(84% 0 16% 0);
    transform: translate3d(0, 0, 0) skew(0.2deg);
  }
}

@media (prefers-reduced-motion: reduce) {
  .site-footer {
    transition: none;
    opacity: 1;
  }

  .site-footer__glitch::before,
  .site-footer__glitch::after {
    animation: none;
    opacity: 0;
  }
}
/* Entrada orquestada con BootIntro.css: el dock aparece a los 1.4s (justo cuando
   el logo termina de irse) ya colapsado (max-width chico, cabe un ícono), y se
   estira hasta los 1.7s. `overflow: hidden` solo mientras dura la animación (con
   un salto discreto a `visible` en 100%): así el ancho se puede recortar durante
   la entrada sin romper el tooltip de cada ícono (que sobresale hacia arriba)
   una vez que la secuencia ya terminó. */
.nav-dock {
  position: fixed;
  left: 50%;
  bottom: 1rem;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 0.375rem;
  padding: 0.4rem;
  border-radius: 9999px;
  border: 1px solid color-mix(in srgb, var(--lightblue) 50%, transparent);
  background-color: color-mix(in srgb, var(--background) 85%, transparent);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  z-index: 50;
  opacity: 0;
  animation: nav-dock-entrance 450ms ease-out 1400ms forwards;
}

/* max-width anima entre dos largos reales (3rem -> 26rem), nunca hacia `none`:
   CSS no puede interpolar un largo contra `none` (no son compatibles), así que
   con `none` como destino el navegador se queda en 3rem casi toda la animación
   y salta de golpe al final ("PUM"). 26rem ya es más ancho que el dock a pleno
   en cualquier breakpoint, así que el estiramiento se ve completo y suave, y el
   dock simplemente llega a su ancho natural antes de que max-width termine de crecer. */
@keyframes nav-dock-entrance {
  0% {
    max-width: 3rem;
    opacity: 0;
    overflow: hidden;
  }
  1% {
    opacity: 1;
  }
  99.9% {
    overflow: hidden;
  }
  100% {
    max-width: 26rem;
    overflow: visible;
    opacity: 1;
  }
}

/* Cada ícono aparece con opacidad, uno por uno de izquierda a derecha, recién
   cuando el dock ya terminó de estirarse (a partir de los 1.7s). */
.nav-dock__item {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 9999px;
  color: var(--white-70);
  opacity: 0;
  animation: nav-dock-item-entrance 250ms ease-out 1850ms forwards;
  transition: color 150ms ease, background-color 150ms ease, box-shadow 150ms ease;
}

@keyframes nav-dock-item-entrance {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

.nav-dock__item:nth-child(2) {
  animation-delay: 1940ms;
}

.nav-dock__item:nth-child(3) {
  animation-delay: 2030ms;
}

.nav-dock__item:nth-child(4) {
  animation-delay: 2120ms;
}

.nav-dock__item:nth-child(5) {
  animation-delay: 2210ms;
}

.nav-dock__item:nth-child(6) {
  animation-delay: 2300ms;
}

.nav-dock__item:nth-child(7) {
  animation-delay: 2390ms;
}

.nav-dock__item:hover,
.nav-dock__item:focus-visible,
.nav-dock__item--active {
  color: var(--darkpink);
  background-color: color-mix(in srgb, var(--darkpink) 12%, transparent);
  outline: none;
}

.nav-dock__item:focus-visible {
  box-shadow: 0 0 0 2px var(--darkpink);
}

.nav-dock__icon {
  width: 1.1rem;
  height: 1.1rem;
}

/* Separador sutil entre íconos, centrado en el hueco del gap del flex.
   El offset es la mitad del gap de cada breakpoint (ver .nav-dock). */
.nav-dock__item:not(:last-child)::after {
  content: '';
  position: absolute;
  top: 50%;
  right: -0.1875rem;
  transform: translate(50%, -50%);
  width: 1px;
  height: 55%;
  background-color: color-mix(in srgb, var(--white-70) 35%, transparent);
  pointer-events: none;
}

.nav-dock__dot {
  position: absolute;
  bottom: 0.05rem;
  left: 50%;
  width: 0.25rem;
  height: 0.25rem;
  border-radius: 9999px;
  background-color: var(--darkpink);
  transform: translateX(-50%) scale(0);
  transition: transform 150ms ease;
}

.nav-dock__item--active .nav-dock__dot {
  transform: translateX(-50%) scale(1);
}

.nav-dock__tooltip {
  position: absolute;
  bottom: calc(100% + 0.6rem);
  left: 50%;
  padding: 0.3rem 0.65rem;
  border-radius: 0.375rem;
  border: 1px solid var(--darkpink);
  background-color: color-mix(in srgb, var(--background) 92%, transparent);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  color: var(--white-90);
  font-family: var(--font-plex);
  font-size: 0.7rem;
  letter-spacing: 0.02em;
  white-space: nowrap;
  opacity: 0;
  transform: translate(-50%, 0.2rem);
  transition: opacity 90ms ease, transform 90ms ease;
  pointer-events: none;
}

.nav-dock__tooltip::before,
.nav-dock__tooltip::after {
  content: '';
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  border: 5px solid transparent;
}

.nav-dock__tooltip::before {
  border-top-color: var(--darkpink);
}

.nav-dock__tooltip::after {
  top: calc(100% - 1px);
  border-width: 4px;
  border-top-color: color-mix(in srgb, var(--background) 92%, transparent);
}

.nav-dock__item:hover .nav-dock__tooltip,
.nav-dock__item:focus-visible .nav-dock__tooltip {
  opacity: 1;
  transform: translate(-50%, 0);
}

@media (min-width: 440px) {
  .nav-dock {
    gap: 0.5rem;
    padding: 0.5rem;
  }

  .nav-dock__item {
    width: 2.5rem;
    height: 2.5rem;
  }

  .nav-dock__icon {
    width: 1.25rem;
    height: 1.25rem;
  }

  .nav-dock__dot {
    bottom: 0.1rem;
    width: 0.3rem;
    height: 0.3rem;
  }

  .nav-dock__item:not(:last-child)::after {
    right: -0.25rem;
  }

  .nav-dock__tooltip {
    font-size: 0.75rem;
    padding: 0.35rem 0.75rem;
  }
}

/* Solo en desktop el dock vive arriba en vez de abajo; el tooltip se voltea
   para abrir hacia abajo (igual que LanguageSelector), si no queda cortado
   contra el borde superior de la pantalla. */
@media (min-width: 1200px) {
  .nav-dock {
    top: 1.25rem;
    bottom: auto;
    gap: 0.75rem;
    padding: 0.6rem 0.9rem;
  }

  .nav-dock__item:not(:last-child)::after {
    right: -0.375rem;
  }

  .nav-dock__tooltip {
    bottom: auto;
    top: calc(100% + 0.6rem);
    transform: translate(-50%, -0.2rem);
  }

  .nav-dock__item:hover .nav-dock__tooltip,
  .nav-dock__item:focus-visible .nav-dock__tooltip {
    transform: translate(-50%, 0);
  }

  .nav-dock__tooltip::before,
  .nav-dock__tooltip::after {
    top: auto;
    bottom: 100%;
  }

  .nav-dock__tooltip::before {
    border-top-color: transparent;
    border-bottom-color: var(--darkpink);
  }

  .nav-dock__tooltip::after {
    bottom: calc(100% - 1px);
    border-top-color: transparent;
    border-bottom-color: color-mix(in srgb, var(--background) 92%, transparent);
  }
}

@media (prefers-reduced-motion: reduce) {
  .nav-dock {
    animation: none;
    max-width: none;
    overflow: visible;
    opacity: 1;
  }

  .nav-dock__item {
    animation: none;
    opacity: 1;
  }

  .nav-dock__item,
  .nav-dock__dot,
  .nav-dock__tooltip {
    transition: none;
  }
}
/* Se revela con un fade simple al hacer scroll (mismo mecanismo que
   AboutSection: IntersectionObserver + gate de "scroll-unlocked" para no
   dispararse antes de que termine la orquestación del banner). */
.tech-stack {
  max-width: 90rem;
  margin: 0 auto;
  padding: 2rem 1.25rem 4rem;
  opacity: 0;
  transition: opacity 600ms ease-out;
}

.tech-stack--visible {
  opacity: 1;
}

.tech-stack__header {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
  text-align: center;
  margin-bottom: 2rem;
}

/* Solo para que el hueco de arriba no se vea tan vacío entre el final de la
   sección anterior y el título: chico a propósito, es un acento, no un ícono
   protagonista. */
.tech-stack__header-icon {
  width: 1.75rem;
  height: 1.75rem;
  margin-bottom: 0.5rem;
  color: var(--lightblue);
}

.tech-stack__subtitle {
  color: var(--white-70);
}

.tech-stack__groups {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.25rem;
}

.tech-stack__group {
  padding: 1.25rem;
  border-radius: 0.75rem;
  border: 1px solid var(--az-grid-line);
  background-color: color-mix(in srgb, var(--background) 60%, transparent);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}

.tech-stack__group--cyan {
  border-color: color-mix(in srgb, var(--lightblue) 50%, transparent);
}

.tech-stack__group--pink {
  border-color: color-mix(in srgb, var(--darkpink) 50%, transparent);
}

.tech-stack__group--green {
  border-color: color-mix(in srgb, var(--green) 50%, transparent);
}

.tech-stack__group-title {
  font-family: var(--font-encode);
  font-weight: 700;
  color: var(--white-90);
  font-size: 1rem;
  line-height: 1.3;
}

.tech-stack__group--cyan .tech-stack__group-title {
  color: var(--lightblue);
}

.tech-stack__group--pink .tech-stack__group-title {
  color: var(--darkpink);
}

.tech-stack__group--green .tech-stack__group-title {
  color: var(--green);
}

.tech-stack__group-caption {
  margin-top: 0.15rem;
  margin-bottom: 1rem;
  font-size: 0.75rem;
}

.tech-stack__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(4.5rem, 1fr));
  gap: 0.75rem;
}

.tech-stack__card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.4rem;
  padding: 0.6rem 0.4rem;
  border-radius: 0.5rem;
  background-color: color-mix(in srgb, var(--background) 40%, transparent);
  opacity: 0;
  transition: background-color 150ms ease;
}

.tech-stack__card:hover {
  background-color: color-mix(in srgb, var(--background) 70%, transparent);
}

/* La ola de aparición va por posición dentro de cada grid, no por grupo: el
   1er logo de TODOS los grupos aparece junto, luego el 2do de todos, etc.
   Como el nth-child aplica igual en cada `.tech-stack__grid`, alcanza con un
   solo set de reglas compartido por todos los grupos. */
.tech-stack--visible .tech-stack__card {
  animation: tech-stack-card-entrance 400ms ease-out forwards;
}

@keyframes tech-stack-card-entrance {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

.tech-stack__card:nth-child(1) {
  animation-delay: 0ms;
}

.tech-stack__card:nth-child(2) {
  animation-delay: 150ms;
}

.tech-stack__card:nth-child(3) {
  animation-delay: 300ms;
}

.tech-stack__card:nth-child(4) {
  animation-delay: 450ms;
}

.tech-stack__card:nth-child(5) {
  animation-delay: 600ms;
}

.tech-stack__card:nth-child(6) {
  animation-delay: 750ms;
}

.tech-stack__card:nth-child(7) {
  animation-delay: 900ms;
}

.tech-stack__card:nth-child(8) {
  animation-delay: 1050ms;
}

.tech-stack__card:nth-child(9) {
  animation-delay: 1200ms;
}

.tech-stack__card:nth-child(10) {
  animation-delay: 1350ms;
}

.tech-stack__card:nth-child(11) {
  animation-delay: 1500ms;
}

.tech-stack__card:nth-child(12) {
  animation-delay: 1650ms;
}

.tech-stack__card-icon {
  width: 1.5rem;
  height: 1.5rem;
  color: var(--white-90);
  flex-shrink: 0;
}

.tech-stack__card-name {
  font-family: var(--font-plex);
  font-size: 0.625rem;
  letter-spacing: 0.01em;
  color: var(--white-70);
  text-align: center;
  line-height: 1.2;
}

@media (min-width: 440px) {
  .tech-stack__card-icon {
    width: 1.75rem;
    height: 1.75rem;
  }

  .tech-stack__card-name {
    font-size: 0.6875rem;
  }
}

@media (min-width: 600px) {
  .tech-stack__groups {
    grid-template-columns: repeat(2, 1fr);
  }
}

@media (min-width: 1200px) {
  .tech-stack {
    padding: 2rem 3rem 6rem;
  }

  .tech-stack__groups {
    grid-template-columns: repeat(3, 1fr);
  }
}

/* width: fit-content (no todo el ancho de la sección): el marco se ajusta
   solo al contenido (label + logos), centrado, no de borde a borde. */
.tech-stack__avoid {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.75rem;
  width: fit-content;
  max-width: 100%;
  margin: 2.5rem auto 0;
  padding: 1.5rem 1rem;
  text-align: center;
  border-top: 1px solid var(--darkpink);
  border-bottom: 1px solid var(--darkpink);
}

.tech-stack__avoid-icons {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 1.25rem;
}

.tech-stack__avoid-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
  /* Apagados a propósito: son las tecnologías que evito, no parte del stack. */
  opacity: 0.5;
  filter: grayscale(1);
}

.tech-stack__avoid-icon {
  width: 1.5rem;
  height: 1.5rem;
  color: var(--white-90);
}

.tech-stack__avoid-name {
  font-family: var(--font-plex);
  font-size: 0.625rem;
  letter-spacing: 0.01em;
  color: var(--white-70);
}

@media (prefers-reduced-motion: reduce) {
  .tech-stack {
    transition: none;
    opacity: 1;
  }

  .tech-stack__card {
    animation: none;
    opacity: 1;
  }
}
