/* =========================================================
   LANHMAR — Digital Marketer — Portfolio site
   Brand palette extracted from the original LANHMAR portfolio deck
   ========================================================= */

/* Font is loaded via <link> tags in each page's <head> (not @import here) —
   @import inside a stylesheet forces the browser to fully fetch and parse
   this CSS file before it can even discover the font request, adding a full
   extra network round-trip before text renders in its final font. Loading
   it as <link rel="preconnect"> + <link rel="stylesheet"> in <head> lets the
   browser discover and fetch the font in parallel with style.css instead. */

:root{
  --purple-deep:   #2D2FEF;
  --purple-mid:    #6A5CFF;
  --purple-soft:   #8F84FF;
  --lav-100:       #F3F1FD;
  --lav-200:       #E7E2FB;
  --lav-300:       #D6CEF7;
  --lav-400:       #B2A4EF;
  --orange:        #FF7A45;
  --orange-dark:   #E85D2A;
  --orange-soft:   #FFD8C2;
  /* Contrast-audited orange text/fill pairs (checked against actual WCAG AA
     4.5:1 for normal-size text, not eyeballed): --orange-text is dark
     enough to read clearly on --orange-soft (4.97:1); --orange-deep is dark
     enough for WHITE text on top of it (5.08:1) — plain --orange-dark
     (3.48:1 with white / 2.63:1 with --orange-text on --orange-soft) fails
     AA and was being used for both, which is what made the hero badge and
     a couple of hover states hard to read. */
  --orange-text:   #A6380D;
  --orange-deep:   #C24418;
  --ink:           #241F45;
  --ink-soft:      #55507A;
  --paper:         #FFFDF9;
  --white:         #FFFFFF;

  /* single font family everywhere (previously Baloo 2 + Poppins) — avoids
     mixed-font rendering glitches (e.g. faux-italic on a display font that
     has no real italic cut) and halves the number of font files the
     browser has to fetch before text looks "final". */
  --font-display: 'Montserrat', system-ui, sans-serif;
  --font-body: 'Montserrat', system-ui, sans-serif;

  --radius: 22px;
  --shadow-soft: 0 10px 30px rgba(45,47,239,.12);
}

*{ box-sizing: border-box; }
html{ scroll-behavior: smooth; }
body{
  margin:0;
  font-family: var(--font-body);
  color: var(--ink);
  background:
    radial-gradient(circle at 15% 8%, rgba(255,122,69,.10), transparent 40%),
    radial-gradient(circle at 90% 15%, rgba(106,92,255,.14), transparent 45%),
    var(--lav-100);
  min-height: 100vh;
}

/* subtle paper-grain overlay, no image needed */
body::before{
  content:"";
  position: fixed; inset:0; pointer-events:none; z-index: 0;
  opacity:.5;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 0.16  0 0 0 0 0.12  0 0 0 0 0.35  0 0 0 0.025 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

a{ color: inherit; text-decoration: none; }
img{ max-width:100%; display:block; }
h1,h2,h3,h4{ font-family: var(--font-display); margin:0 0 .5em; color: var(--purple-deep); }
p{ line-height:1.65; margin: 0 0 1em; color: var(--ink-soft); }
ul{ padding-left: 1.1em; margin:0 0 1em; }
li{ margin-bottom:.6em; line-height:1.6; color: var(--ink-soft); }

.wrap{ max-width: 1080px; margin:0 auto; padding: 0 24px; position:relative; z-index:1; }

/* ---------- marquee banner ---------- */
.marquee{
  overflow:hidden; white-space:nowrap;
  background: var(--purple-mid);
  color:#fff;
  padding: 10px 0;
  position: relative; z-index: 2;
}
.marquee__track{
  display:inline-flex; gap: 40px;
  animation: marquee 22s linear infinite;
}
.marquee__track span{
  font-family: var(--font-display);
  font-weight:700; letter-spacing:.06em; font-size: 15px;
  display:inline-flex; align-items:center; gap:10px;
}
.marquee__track span.accent{
  color: #fff;
  /* a little twinkling glow on the ★ separators — cheap, high-visibility
     "creative marketer" sparkle since the marquee runs on every page */
  animation: accent-shimmer 2.4s ease-in-out infinite;
}
@keyframes marquee{
  from{ transform: translateX(0); }
  to{ transform: translateX(-50%); }
}
@keyframes accent-shimmer{
  0%, 100% { color:#fff; text-shadow: 0 0 6px rgba(255,255,255,.45); }
  50%      { color: var(--orange-soft); text-shadow: 0 0 12px rgba(255,122,69,.85); }
}
/* The running marquee banner is deliberately EXCLUDED from this rule (per
   Lan Anh's explicit choice) — it's the site's signature "LANHMAR" brand
   strip and should always scroll, even on devices/browsers with "reduce
   motion" turned on at the OS level (this is why it looked frozen on some
   desktops but animated fine on phones: prefers-reduced-motion was true on
   the desktop, false on the phone). Other, more intense effects (badge
   pulse, banner image animation) still respect the accessibility setting. */
@media (prefers-reduced-motion: reduce){
  .hero__badge{ animation: none; }
  .banner-strip img{ animation: none; }
}

/* ---------- header / nav ---------- */
header.site{
  position: sticky; top:0; z-index: 30;
  background: rgba(243,241,253,.85);
  backdrop-filter: blur(8px);
  border-bottom: 2px solid var(--lav-300);
}
.nav{
  display:flex; align-items:center; justify-content:space-between;
  padding: 14px 24px; max-width: 1080px; margin:0 auto; gap: 16px; flex-wrap: wrap;
}
.brand{ display:flex; align-items:center; gap:10px; }
.brand img{ height: 34px; width:auto; }
.brand small{ display:block; font-size:11px; color: var(--ink-soft); letter-spacing:.12em; }

.nav__links{ display:flex; align-items:center; gap: 6px; flex-wrap: wrap; }
.nav__links a{
  font-family: var(--font-display); font-weight:600; font-size: 14.5px;
  padding: 8px 14px; border-radius: 999px; color: var(--purple-deep);
  transition: background .15s ease, color .15s ease;
}
.nav__links a:hover{ background: var(--lav-300); }
.nav__links a.is-active{ background: var(--purple-deep); color:#fff; }

.lang-toggle{
  display:flex; border: 2px solid var(--purple-deep); border-radius: 999px; overflow:hidden;
}
.lang-toggle button{
  font-family: var(--font-display); font-weight:700; font-size: 13px;
  padding: 7px 14px; background: transparent; border:none; cursor:pointer; color: var(--purple-deep);
}
.lang-toggle button.is-on{ background: var(--purple-deep); color:#fff; }

/* ---------- banner strip (About Me) — a designed portrait/badge graphic,
   shown edge-to-edge (full width) so no backdrop shows on the sides; a
   little vertical padding keeps a sliver of the soft brand backdrop above
   and below only, which reads as intentional framing rather than a
   lopsided gap. ---------- */
.banner-strip{
  width:100%; position:relative; z-index:1; overflow:hidden;
  padding: 10px 0;
  background:
    radial-gradient(circle at 20% 20%, rgba(106,92,255,.16), transparent 55%),
    radial-gradient(circle at 82% 75%, rgba(255,122,69,.14), transparent 50%),
    var(--lav-200);
  border-bottom: 3px solid var(--lav-300);
}
.banner-strip img{
  display:block;
  width:100%; height:auto;
  filter: drop-shadow(0 10px 20px rgba(45,47,239,.18));
  /* slow continuous "Ken Burns" drift — a living, camera-panning feel for
     the hero banner instead of a static poster; overflow:hidden on the
     parent strip keeps the zoomed edges from ever showing. */
  animation: banner-kenburns 16s ease-in-out infinite;
  transform-origin: 50% 35%;
}
@keyframes banner-kenburns{
  0%   { transform: scale(1); }
  50%  { transform: scale(1.055); }
  100% { transform: scale(1); }
}
/* full-width block matching the now-edge-to-edge image exactly, so the
   DOB/MBTI badges below can be positioned as percentages that always land
   on the banner artwork itself, at any viewport width. */
.banner-strip__frame{ position:relative; display:block; width:100%; line-height:0; }

/* DOB/MBTI badges overlaid directly on the banner artwork, in its two
   empty top corners (clear of the LANHMAR/"RANH MA" text, the ID-badge
   photo, and the PORTFOLIO logo in the bottom-right) */
.banner-badge{
  position:absolute; top: 6%;
  /* plain inline-block, not flex — this wraps mixed text nodes + spans
     (label text, ": " separator, value span), and flexbox has a habit of
     collapsing the whitespace-only text between adjacent flex items */
  display:inline-block;
  background: rgba(255,255,255,.85);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  border: 2px solid var(--purple-soft);
  color: var(--purple-deep);
  font-family: var(--font-display); font-weight:700; font-size: 14px;
  padding: 7px 16px; border-radius: 999px;
  box-shadow: var(--shadow-soft);
  white-space: nowrap; cursor: default;
  transition: background .25s ease, color .25s ease, border-color .25s ease, transform .2s ease;
}
.banner-badge:hover{
  background: var(--orange-deep); border-color: var(--orange-deep); color:#fff;
  transform: translateY(-3px);
}
.banner-badge--dob{ left: 3%; }
.banner-badge--mbti{ right: 3%; }
@media (max-width:760px){
  .banner-strip{ padding:8px 0; }
  .banner-badge{ font-size: 10.5px; padding: 5px 11px; border-width:1.5px; top:4%; }
}
@media (max-width:480px){ .banner-badge{ font-size: 8.5px; padding:4px 8px; } }

/* ---------- hero ---------- */
.hero{
  position:relative; z-index:1;
  padding: 64px 0 40px;
}
.hero__grid{
  display:grid; grid-template-columns: 1.2fr .8fr; gap: 40px; align-items:center;
}
@media (max-width: 760px){ .hero__grid{ grid-template-columns: 1fr; } }

.hero__badge{
  display:inline-flex; align-items:center; gap:8px;
  background: var(--orange-soft); color: var(--orange-text);
  font-family: var(--font-display); font-weight:700; font-size: 13px;
  padding: 6px 14px; border-radius: 999px; margin-bottom: 18px;
  /* soft expanding pulse ring — a little "alive" attention-grabber on the
     page's opening badge, without ever touching text/background color so
     contrast never dips */
  animation: badge-pulse 3.2s ease-in-out infinite;
}
@keyframes badge-pulse{
  0%, 100% { box-shadow: 0 0 0 0 rgba(255,122,69,.38); }
  50%      { box-shadow: 0 0 0 7px rgba(255,122,69,0); }
}
.hero h1{
  font-size: clamp(34px, 6vw, 56px);
  line-height: 1.05;
  margin-bottom: 14px;
}
.hero h1 .script{
  color: var(--orange); font-style: italic;
  text-decoration: underline solid var(--orange-soft) 6px;
  text-underline-offset: 6px;
}
.hero__lead{ font-size: 17px; max-width: 46ch; }
.hero__photo-wrap{
  position:relative; display:flex; justify-content:center;
}
.hero__photo{
  width: min(320px, 80%);
  /* The <img> tags carry width/height HTML attributes (used as loading
     hints to reserve space before the image decodes) — those map to a
     low-priority "height" presentational hint that would otherwise pin
     the box to the original photo's tall intrinsic height and silently
     defeat aspect-ratio below. height:auto clears that hint so aspect-
     ratio is the one actually deciding the box's height. */
  height: auto;
  /* The source photos are tall portrait crops (e.g. 788×1400) — at the
     card's fixed width that used to stretch the whole hero section down
     well past the text column next to it. Locking a shorter, consistent
     portrait ratio and cropping with object-fit keeps every hero photo
     (About Me, Contact) the same comfortable height regardless of the
     original photo's proportions. object-position leans toward the top
     since these are headshot-style crops — face/shoulders stay in frame. */
  aspect-ratio: 3 / 4;
  object-fit: cover;
  object-position: 50% 12%;
  border-radius: 28px;
  border: 4px dashed var(--purple-mid);
  padding: 10px;
  background: var(--lav-200);
  box-shadow: var(--shadow-soft);
  transition: transform .3s ease, box-shadow .3s ease;
}
.hero__photo:hover{ transform: scale(1.035); box-shadow: 0 16px 30px rgba(45,47,239,.22); }
.hero__mascot{
  position:absolute; width: 110px; right: -6px; top: -18px;
  filter: drop-shadow(0 8px 14px rgba(45,47,239,.25));
  transform: rotate(8deg);
}
@media (max-width:760px){ .hero__mascot{ width:84px; right: 10%; } }
.hero__typo-deco{
  position:absolute; width: 128px; left: -14px; bottom: 6px;
  filter: drop-shadow(0 6px 12px rgba(45,47,239,.18));
  transform: rotate(-7deg);
}
@media (max-width:760px){ .hero__typo-deco{ width:96px; left:2%; } }

.btn-row{ display:flex; gap:12px; flex-wrap:wrap; margin-top: 22px; }
.btn{
  display:inline-flex; align-items:center; gap:8px;
  font-family: var(--font-display); font-weight:700; font-size:14.5px;
  padding: 12px 22px; border-radius: 999px; border: 2px solid transparent; cursor:pointer;
  transition: background .25s ease, color .25s ease, border-color .25s ease,
              transform .2s ease, box-shadow .25s ease;
}
.btn--primary{ background: var(--purple-deep); color:#fff; }
.btn--primary:hover{
  background:#1f21c9; transform: translateY(-3px) scale(1.03);
  box-shadow: 0 12px 22px rgba(45,47,239,.30);
}
.btn--ghost{ border-color: var(--purple-deep); color: var(--purple-deep); background:#fff; }
.btn--ghost:hover{
  background: var(--lav-200); transform: translateY(-3px) scale(1.03);
  box-shadow: var(--shadow-soft);
}
.btn:active{ transform: translateY(-1px) scale(1.01); }

/* ---------- torn paper section ---------- */
.torn-section{
  position: relative; z-index:1;
  background: var(--paper);
  padding: 56px 0 64px;
  clip-path: polygon(
    0% 2%, 3% 0%, 8% 1.6%, 14% .3%, 20% 1.8%, 27% .2%, 34% 1.4%, 41% .1%, 48% 1.7%,
    55% .4%, 62% 1.9%, 69% .2%, 76% 1.5%, 83% .3%, 90% 1.8%, 96% .2%, 100% 1.4%,
    100% 98.5%, 95% 100%, 89% 98.3%, 82% 99.7%, 75% 98.2%, 68% 99.9%, 61% 98.4%,
    54% 99.8%, 47% 98.1%, 40% 99.9%, 33% 98.3%, 26% 99.8%, 19% 98.2%, 12% 99.9%,
    6% 98.4%, 0% 99.7%
  );
  margin: -18px 0;
}
.torn-section + .torn-section{ background: var(--lav-200); }

.section-title{ text-align:center; margin-bottom: 34px; }
.section-title h2{ font-size: clamp(26px, 4vw, 38px); }
.section-title .kicker{
  display:inline-block; font-family: var(--font-display); font-weight:700;
  color: var(--orange-text); font-size:14px; letter-spacing:.14em; text-transform:uppercase; margin-bottom:6px;
}

/* ---------- pill / tag ---------- */
.tag-row{ display:flex; flex-wrap:wrap; gap:10px; }
.tag{
  display:inline-block; padding: 8px 16px; border-radius: 999px;
  background: var(--lav-200); border: 2px solid var(--lav-400);
  font-weight:600; font-size: 14px; color: var(--purple-deep);
  transition: background .25s ease, border-color .25s ease, color .25s ease, transform .2s ease;
}
.tag:hover{
  background: var(--orange-soft); border-color: var(--orange);
  color: var(--orange-text); transform: translateY(-2px) scale(1.05);
}
/* --purple-soft/--orange (used elsewhere as light accent fills) are too
   light for white text to clear WCAG AA at this size — --purple-deep and
   --orange-deep are the contrast-checked equivalents for white-on-solid. */
.tag--solid{ background: var(--purple-deep); border-color: var(--purple-deep); color:#fff; }
.tag--solid:hover{ background: var(--orange-deep); border-color: var(--orange-deep); color:#fff; }

/* ---------- cards ---------- */
.grid{ display:grid; gap: 22px; }
.grid--2{ grid-template-columns: repeat(2, 1fr); }
.grid--3{ grid-template-columns: repeat(3, 1fr); }
@media (max-width: 820px){ .grid--2, .grid--3{ grid-template-columns: 1fr; } }
/* single-certificate accordion group (e.g. Canva Academy, Gemini) — a lone
   card stretched to a 2-column grid would leave an awkward empty half-row,
   so it gets a centered, column-width-ish max-width instead. */
.grid--1{ grid-template-columns: minmax(0, 520px); justify-content:center; }

.card{
  background:#fff; border-radius: var(--radius); padding: 26px;
  box-shadow: var(--shadow-soft); border: 2px solid var(--lav-300);
}

/* folder-tab style card, echoes the original "project folder" motif */
.folder-card{
  position:relative; background:#fff; border-radius: 18px;
  border: 2px solid var(--lav-300); overflow:hidden;
  box-shadow: var(--shadow-soft);
  display:flex; flex-direction:column;
}
.folder-card__tab{
  position:relative;
  background: linear-gradient(135deg, var(--orange) 0%, var(--orange-soft) 100%);
  color:#fff; font-family: var(--font-display); font-weight:800;
  padding: 14px 20px; font-size: 20px; display:flex; align-items:center; gap:10px;
}
.folder-card__tab .num{
  font-size:13px; background: rgba(255,255,255,.35); border-radius:999px;
  width:26px; height:26px; display:inline-flex; align-items:center; justify-content:center;
}
.folder-card img{ width:100%; height: 190px; object-fit: cover; }
.folder-card__body{ padding: 18px 20px 22px; }
/* Brief -> Process (above the carousel) -> Output (below it), kept compact
   so the card stays image-led — text is the ~30%, the carousel is the ~70%.
   "Đề bài" + "Cách làm" are clamped to a few lines with a "Xem thêm" toggle
   (wireBriefToggles() in script.js only reveals the button when the text
   actually overflows the clamp) so a long brief never dwarfs the carousel. */
.folder-card__brief{ padding: 16px 20px 0; }
.folder-card__brief-text{
  font-size:13.5px; line-height:1.65; color: var(--ink-soft);
  margin:0;
  display:-webkit-box; -webkit-line-clamp:4; -webkit-box-orient:vertical; overflow:hidden;
}
/* Bug fix: -webkit-line-clamp requires display:-webkit-box while clamped.
   Unsetting line-clamp alone (without also leaving -webkit-box mode) made
   the "Xem thêm"-expanded text lay out as if each word were a separate
   flex/box child instead of a normal wrapped paragraph — e.g. "Brief:"
   would stay put while the rest of the sentence visibly broke onto its own
   misaligned line. display:block on expand restores normal text flow. */
.folder-card__brief.is-expanded .folder-card__brief-text{
  display: block; -webkit-line-clamp: unset; overflow: visible;
}
.folder-card__brief-text strong{ color: var(--ink); }
.folder-card__brief-toggle{
  display:block; margin: 10px 0 0; background:none; border:none; padding:0;
  font-family: var(--font-display); font-weight:700; font-size:12.5px;
  color: var(--purple-mid); cursor:pointer;
}
.folder-card__brief-toggle:hover{ text-decoration:underline; }
/* Kết quả / Result — sits below the carousel, always shown in full (never
   clamped) since it's usually short. */
.folder-card__result{
  font-size:13.5px; line-height:1.65; color: var(--ink-soft);
  margin: 6px 0 10px;
}
.folder-card__result strong{ color: var(--ink); }

/* certificate cards reuse .card but need edge-to-edge media on top */
.cert-card{ padding:0; overflow:hidden; display:flex; flex-direction:column; }
.cert-card__body{ padding: 18px 20px 22px; }

/* ---------- accordion (one per certificate org — SEONGON, Canva, Gemini, etc.) ---------- */
.accordion{
  background:#fff; border-radius: var(--radius); border: 2px solid var(--lav-300);
  box-shadow: var(--shadow-soft); overflow:hidden; margin-bottom: 26px;
  /* per-org accent (set inline via --accent, an "R, G, B" triplet sampled
     straight from that org's own logo) tints the border so each accordion
     reads as belonging to its certifying partner even before it's opened */
  border-color: rgba(var(--accent, 124, 108, 224), .35);
}
.accordion__header{
  width:100%; display:flex; align-items:center; gap:16px;
  padding: 18px 22px;
  background: linear-gradient(120deg, rgba(var(--accent, 124, 108, 224), .16), rgba(var(--accent, 124, 108, 224), .05));
  border:none; cursor:pointer; text-align:left;
}
.accordion__brand{ display:flex; flex-direction:column; gap:6px; flex:1; min-width:0; }
.accordion__title{ font-family: var(--font-display); font-weight:700; font-size: 17px; color: var(--ink); }
/* the certifying org's own logo — sized as a wide chip (not a small square)
   since two of the three partner logos are wordmarks, and given a white
   backing card so transparent-background logos stay legible regardless of
   the tinted header behind them */
.accordion__deco{
  width:132px; height:58px; padding:8px 12px;
  background:#fff; border-radius:14px; object-fit:contain;
  box-shadow: var(--shadow-soft); flex-shrink:0;
  transition: opacity .25s ease, width .25s ease, padding .25s ease, margin .25s ease;
}
.accordion.is-open .accordion__deco{ opacity:0; width:0; padding:0; margin:0; }
.accordion__chevron{
  font-family: var(--font-display); font-size: 20px; color: rgb(var(--accent, 124, 108, 224));
  flex-shrink:0; transition: transform .3s ease;
}
.accordion.is-open .accordion__chevron{ transform: rotate(180deg); }
.accordion__panel{
  max-height:0; overflow:hidden;
  transition: max-height .4s ease;
}
.accordion__panel-inner{ padding: 24px 22px 26px; }
@media (max-width:560px){
  .accordion__header{ padding:14px 16px; gap:10px; }
  .accordion__title{ font-size:15px; }
  .accordion__deco{ width:92px; height:46px; padding:6px 10px; }
}

/* ---------- swipeable image/video carousel (projects + certificates) ---------- */
.carousel{ position:relative; overflow:hidden; background: var(--lav-200); }
.carousel__track{
  display:flex; overflow-x:auto; scroll-snap-type:x mandatory;
  scrollbar-width:none; -webkit-overflow-scrolling:touch;
}
.carousel__track::-webkit-scrollbar{ display:none; }
.carousel__slide{ flex:0 0 100%; scroll-snap-align:start; }
.carousel__slide img{
  width:100%; height:230px; object-fit:contain; object-position:center; display:block;
  background: var(--lav-200);
  transition: transform .45s ease;
}
/* Applied by LANHMAR_imgFallback() (script.js) when a Sheet-provided image
   URL fails to load — swaps in a plain placeholder graphic instead of the
   browser's broken-image icon; object-fit:contain keeps it centered and
   modestly sized instead of stretching to fill the frame. */
.img-fallback{ object-fit: contain !important; padding: 24px; }
/* subtle hover zoom on whichever slide is currently visible — overflow:hidden
   on .carousel keeps the enlarged image cleanly clipped to the card */
.carousel:hover .carousel__slide img{ transform: scale(1.06); }
@media (max-width:520px){ .carousel__slide img{ height:200px; } }
.carousel__btn{
  position:absolute; top:50%; transform:translateY(-50%);
  width:32px; height:32px; border-radius:50%; border:none;
  background:rgba(36,31,69,.55); color:#fff; font-size:18px; line-height:1;
  cursor:pointer; display:flex; align-items:center; justify-content:center; z-index:2;
}
.carousel__btn--prev{ left:8px; }
.carousel__btn--next{ right:8px; }
.carousel__btn:hover{ background:rgba(36,31,69,.8); }
.carousel__dots{
  position:absolute; bottom:8px; left:0; right:0;
  display:flex; justify-content:center; gap:6px; z-index:2;
}
.carousel__dot{
  width:26px; height:26px; border-radius:50%;
  background:transparent; border:none; cursor:pointer; padding:0;
  display:flex; align-items:center; justify-content:center;
}
.carousel__dot::before{
  content:""; width:7px; height:7px; border-radius:50%;
  background:rgba(255,255,255,.65); border:1px solid rgba(36,31,69,.35);
}
.carousel__dot.is-active::before{ background: var(--orange); border-color: var(--orange); }
.carousel__video{ position:relative; display:block; height:100%; }
.carousel__video img{ filter:brightness(.7); }
.carousel__play{
  position:absolute; top:50%; left:50%; transform:translate(-50%,-50%);
  width:48px; height:48px; border-radius:50%; background:rgba(255,255,255,.92); color: var(--purple-deep);
  display:flex; align-items:center; justify-content:center; font-size:19px; padding-left:3px;
}
.carousel__video-label{
  position:absolute; bottom:10px; left:10px; right:10px; color:#fff; font-weight:700;
  font-size:12.5px; text-shadow: 0 1px 4px rgba(0,0,0,.65);
}

/* per-slide caption box — swapped in place by wireCarousels() as the
   visitor scrolls the carousel above, so the text always matches the
   currently-visible image/video */
.carousel-caption{ min-height: 1em; }
.carousel-caption p{ margin:0; }
.carousel-caption__list{
  margin:0; padding-left: 20px; display:flex; flex-direction:column; gap:8px;
}
.carousel-caption__list li{ font-size: inherit; margin-bottom:0; }
.carousel-plan-link{ margin-top:14px; }

/* Overlay caption bar (projects.html only — buildCarousel's opts.overlay):
   the same .carousel-caption element used elsewhere, but placed INSIDE the
   carousel by buildCarousel() and styled here as a bottom gradient-scrim bar
   so a slide's caption reads directly on top of the image/video as the
   visitor drags/swipes, instead of taking up card height as a separate box
   below the carousel. Clamped to 2 lines — captions are written short, but
   this is a hard safety net against any one running long. */
.carousel__overlay{
  position:absolute; left:0; right:0; bottom:0; z-index:2;
  display:flex; flex-direction:column; align-items:center; gap:4px;
  padding: 34px 14px 10px;
  background: linear-gradient(to top, rgba(20,15,50,.86) 0%, rgba(20,15,50,.55) 60%, rgba(20,15,50,0) 100%);
  pointer-events:none; /* lets clicks/drags on the image itself pass through */
}
.carousel__overlay .carousel-caption{ min-height:0; width:100%; }
.carousel__overlay .carousel-caption p{
  margin:0; color:#fff; font-size:12.5px; font-weight:600; line-height:1.4; text-align:center;
  text-shadow: 0 1px 3px rgba(0,0,0,.55);
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden;
}
.carousel__overlay .carousel-caption__list{ text-align:left; }
.carousel__overlay .carousel__dots{ position:static; pointer-events:auto; }

/* ---------- experience timeline (centered line, alternating sides) ----------
   Desktop: a vertical line runs down the middle of .timeline; each
   .timeline-row places its .timeline-item on the left or right of it
   (--left / --right, set per-entry in experience.html) with a .timeline-dot
   marking its spot on the line. Falls back to the older single left-aligned
   column (line + dots on the left, cards full-width) below 760px, where
   splitting entries across two narrow columns would be harder to read than
   just stacking them. */
.timeline{ position:relative; padding: 6px 0; }
.timeline::before{
  content:""; position:absolute; left:50%; top:0; bottom:0; width:3px;
  background: linear-gradient(var(--purple-mid), var(--orange));
  border-radius: 3px; transform: translateX(-50%);
}
.timeline-row{ position:relative; margin-bottom: 40px; }
.timeline-row:last-child{ margin-bottom: 0; }
.timeline-dot{
  position:absolute; left:50%; top:6px; width:18px; height:18px;
  border-radius:50%; background: var(--orange); border:3px solid #fff;
  box-shadow: 0 0 0 3px var(--purple-mid); transform: translateX(-50%); z-index:1;
}
.timeline-item{ position:relative; width: calc(50% - 42px); }
.timeline-row--left .timeline-item{ margin-right: auto; }
.timeline-row--right .timeline-item{ margin-left: auto; }
@media (max-width:760px){
  .timeline::before{ left:6px; transform:none; }
  .timeline-dot{ left:6px; transform:none; }
  .timeline-item{ width:100%; margin-left:26px !important; margin-right:0 !important; }
}
.timeline-item .period{
  display:inline-block; font-family: var(--font-display); font-weight:700; font-size:13px;
  color:#fff; background: var(--purple-deep); padding:4px 12px; border-radius:999px; margin-bottom:8px;
}
.timeline-item h3{ margin-bottom: 4px; font-size: 21px; }
.timeline-item .org{ color: var(--orange-text); font-weight:600; margin-bottom: 12px; display:block; }
.timeline-item h4{ font-size: 14px; text-transform:uppercase; letter-spacing:.08em; color: var(--ink-soft); margin: 20px 0 10px; }

.timeline-item__head{ display:flex; align-items:center; gap:12px; margin-bottom:4px; }
.timeline-item__logo{
  width:48px; height:48px; border-radius:12px; object-fit:contain; background:#fff;
  border:2px solid var(--lav-300); padding:5px; flex-shrink:0;
}
.timeline-item__work{
  width:100%; max-width:420px; height:220px; object-fit:cover; border-radius:16px;
  margin: 16px 0 12px; box-shadow: var(--shadow-soft); border:2px solid var(--lav-300);
  transition: transform .3s ease, box-shadow .3s ease;
}
.timeline-item__work:hover{ transform: scale(1.035); box-shadow: 0 14px 26px rgba(45,47,239,.20); }
@media (max-width:520px){ .timeline-item__work{ height:180px; } }
/* "context/brief" line — the problem the role was there to solve, stated
   before the task list so a reader gets the "why" before the "what" */
.timeline-item__context{
  font-size:14.5px; line-height:1.6; color: var(--ink-soft); font-style:italic;
  margin: 10px 0 16px; padding-left:12px; border-left: 3px solid var(--lav-300);
}

/* ---------- footer ---------- */
footer.site{
  position:relative; z-index:1;
  background: var(--purple-deep); color: #fff; padding: 34px 0; margin-top: 40px;
}
footer.site .wrap{ display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap: 14px; }
footer.site a{ color:#fff; opacity:.85; }
footer.site a:hover{ opacity:1; text-decoration: underline; }
.social-row{ display:flex; gap: 14px; }
.social-row a{
  display:inline-flex; align-items:center; gap:8px;
  background: rgba(255,255,255,.12); padding:8px 16px; border-radius:999px; font-weight:600; font-size:14px;
}
.social-row a:hover{ background: rgba(255,255,255,.22); }
/* groups the social links + the "PORTFOLIO" wordmark accent together on the
   right side of the footer bar, so footer.site .wrap keeps exactly two flex
   children (copyright / this group) and space-between still lands the group
   flush in the right corner instead of splitting three items apart. */
.footer-right{ display:flex; align-items:center; gap:20px; flex-wrap:wrap; }
.footer-typo{ height:22px; width:auto; opacity:.9; }
@media (max-width:640px){ .footer-typo{ display:none; } }

/* ---------- contact page ---------- */
.contact-card{
  display:grid; grid-template-columns: 1fr 1fr; gap: 30px; align-items:center;
}
@media (max-width:760px){ .contact-card{ grid-template-columns:1fr; } }
.contact-list{ list-style:none; padding:0; }
.contact-list li{
  display:flex; align-items:center; gap:14px; margin-bottom:16px; font-weight:600; color: var(--ink);
  min-width:0;
}
.contact-list li a,
.contact-list li > span:not(.ico){
  overflow-wrap: anywhere; word-break: break-word; min-width:0;
}
.contact-list .ico{
  width:42px; height:42px; border-radius:50%; background: var(--lav-200);
  display:flex; align-items:center; justify-content:center; font-size:19px; flex-shrink:0;
}
.contact-list .ico img{ width:24px; height:24px; object-fit:contain; }
/* No standalone LinkedIn PNG asset exists yet — LinkedIn's own mark IS the
   two-letter "in" wordmark, so instead of a stray unstyled "in" that reads
   like a typo next to the real icon images, style it explicitly as the
   brand badge (white bold "in" on LinkedIn blue). */
.contact-list .ico.ico--linkedin{
  background: #0A66C2; color:#fff; font-weight:800; font-size:16px; letter-spacing:.2px;
}
.contact-col{ position:relative; }

/* ---------- small corner mascot accent (simple hero pages) ---------- */
.hero__corner-mascot{
  position:absolute; width: 92px; right: 6%; top: 6px;
  filter: drop-shadow(0 8px 14px rgba(45,47,239,.2));
  transform: rotate(8deg);
}
@media (max-width:760px){ .hero__corner-mascot{ display:none; } }

.hero__corner-deco{
  position:absolute; width: 66px; right: 12%; top: 96px;
  border-radius:14px; object-fit:cover;
  filter: drop-shadow(0 8px 14px rgba(45,47,239,.2));
  transform: rotate(9deg); opacity:.92;
}
@media (max-width:760px){ .hero__corner-deco{ display:none; } }

/* ---------- utility ---------- */
.center{ text-align:center; }
.mt-0{ margin-top:0; }
[data-i18n-hide-if-empty].is-empty{ display:none; }
.lang-vi [data-lc="en"]{ display:none; }
.lang-en [data-lc="vi"]{ display:none; }
