/* ---- Pixel display font ----
   "Minecraft" by Idrees Hassan (github.com/IdreesInc/Minecraft-Font),
   SIL Open Font License 1.1 -- free to bundle/redistribute, unlike the
   Minecraftia font (personal-use-only, commercial use needs a paid
   license from its author). Full license + copyright notice retained
   at assets/fonts/LICENSE-Minecraft-Font.txt per the OFL's terms. */
@font-face{
  font-family: 'MCPixel';
  src: url('../assets/fonts/Minecraft.otf') format('opentype');
  font-weight: 400;
  font-display: swap;
}
@font-face{
  font-family: 'MCPixel';
  src: url('../assets/fonts/Minecraft-Bold.otf') format('opentype');
  font-weight: 700;
  font-display: swap;
}

/* ---- Tokens ---- */
:root{
  --void: #0F0F12;
  --dirt: #6B4A32;
  --grass: #5B8C3A;
  --stone: #8B8B8B;
  --diamond: #4CD3D3;
  --gold: #E8B84B;
  --offwhite: #E7E4DA;

  --sakura-light: #e78fc0;
  --title-ink: #141414;

  /* Press Start 2P (Google Fonts) stays as the fallback if the local
     file fails to load -- also OFL-licensed, so the fallback chain
     never drops into an unlicensed or generic modern font. */
  --font-display: 'MCPixel', 'Press Start 2P', monospace;
  --font-body: 'IBM Plex Mono', ui-monospace, monospace;

  --ease: cubic-bezier(0.2, 0.7, 0.3, 1);
}

*{ box-sizing: border-box; }

/* The whole app is a single fixed-viewport shell -- About/Experience/
   Projects are swapped full-screen panels, not sections you scroll to.
   No white margins, no scrolling, ever. */
html, body{ height: 100%; overflow: hidden; }

@media (prefers-reduced-motion: reduce){
  *, *::before, *::after{
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
}
/* The settings panel's "Reduce Motion" toggle offers the same thing as a
   manual, persistent choice, independent of the OS-level setting. */
html.motion-off .ts-petal,
html.motion-off .ts-splash,
html.motion-off .ts-sprite{
  animation: none !important;
}
/* The panorama's own rotation isn't a CSS animation (js/panorama.js reads
   this same .motion-off class directly and freezes its rotation loop),
   so there's nothing for the global rule above to target there. */

body{
  margin: 0;
  background: var(--void);
  color: var(--offwhite);
  font-family: var(--font-body);
  line-height: 1.6;
}

a{ color: inherit; }
button{ font: inherit; }

/* ---- Loading screen: studio-logo startup splash ----
   Dark screen, a wide red banner with a big white pixel "HUY" (hard drop
   shadow) over "STUDIOS", and along the bottom the cycling status line,
   a thin red progress bar and the copyright. Scales with the viewport. */
.loading-screen{
  --ls: min(1vw, 1.6vh);
  --loading-red: #d93a3f;
  position: fixed;
  inset: 0;
  z-index: 100;
  background: #141414;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  transition: opacity 500ms var(--ease);
}
.loading-screen.fade-out{ opacity: 0; pointer-events: none; }

.loading-logo{
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(0.6 * var(--ls));
  padding: calc(2.6 * var(--ls)) calc(7 * var(--ls)) calc(2.2 * var(--ls));
  background: var(--loading-red);
  color: #ffffff;
  font-weight: 700;
  animation: loading-logo-in 600ms var(--ease) both;
}
.loading-logo-name{
  /* Press Start 2P, not MCPixel: MCPixel's bold Y has a flat top and
     reads as a T at this size. */
  font-family: 'Press Start 2P', var(--font-display);
  font-weight: 400;
  font-size: calc(8.2 * var(--ls));
  line-height: 1;
  letter-spacing: 0.12em;
  margin-right: -0.12em;
  text-shadow: calc(0.55 * var(--ls)) calc(0.55 * var(--ls)) 0 rgba(0, 0, 0, 0.45);
}
.loading-logo-sub{
  font-size: calc(2.5 * var(--ls));
  letter-spacing: 0.4em;
  margin-right: -0.4em;
  text-shadow: calc(0.25 * var(--ls)) calc(0.25 * var(--ls)) 0 rgba(0, 0, 0, 0.35);
}
@keyframes loading-logo-in{ from{ opacity: 0; } to{ opacity: 1; } }

.loading-bottom{
  position: absolute;
  left: 50%;
  bottom: calc(5 * var(--ls));
  transform: translateX(-50%);
  width: min(46vw, 640px);
  min-width: 240px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(1.4 * var(--ls));
}
.loading-label{
  margin: 0;
  font-size: max(11px, calc(1.2 * var(--ls)));
  letter-spacing: 0.08em;
  color: #c9c9c9;
}
.loading-bar-track{
  width: 100%;
  height: 4px;
  background: #3a3a3a;
}
.loading-bar-fill{
  height: 100%;
  width: 0%;
  background: var(--loading-red);
  animation: loading-fill 2.1s ease-in-out forwards;
}
@keyframes loading-fill{ to{ width: 100%; } }
.loading-copyright{
  margin: 0;
  font-size: max(9px, calc(0.95 * var(--ls)));
  letter-spacing: 0.06em;
  white-space: nowrap;
  color: #6f6f6f;
}

/* ---- Titlescreen: exactly one viewport, nothing scrolls ---- */
.titlescreen{
  position: fixed;
  inset: 0;
  height: 100vh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Top-anchored with a fluid vh offset instead of pure centering --
     puts the logo/menu block in the upper-middle of the screen with
     comfortable headroom, closer together, matching a real Minecraft
     menu's composition instead of dead-center + wide gap. Scales with
     viewport height so short windows (720p-ish) don't get pushed into
     overflow before the menu even starts. */
  justify-content: center;
  /* --u: the title screen is designed at 1440x900 and scales as ONE
     piece with the window's WIDTH -- 1px of design per 1/1440 of the
     viewport width -- so zooming or narrowing the window never
     rearranges the composition, it just scales it (like a game's menu).
     Only a window shorter than 820u (e.g. very wide and short) switches
     to height, so the logo + menu never run off the bottom. Every size
     on the title screen is a multiple of --u. */
  --u: min(calc(100vw / 1440), calc(100vh / 820));
  /* Logo + menu are centred vertically, lifted by 71u (half of this
     142u) so at 1440x900 they sit exactly where the design puts them
     (logo top 63u from the top). */
  padding-bottom: calc(142 * var(--u));
  text-align: center;
  opacity: 0;
  transition: opacity 600ms var(--ease);
  z-index: 1;
}
.titlescreen.visible{ opacity: 1; }
.titlescreen.ts-hidden{ display: none; }

/* ---- Background, fallback layer: the real looping video ----
   object-fit:cover is the <video> equivalent of background-size:cover.
   No CSS pan/translate here anymore -- a flat plane sliding sideways
   was never a real panorama, just a moving rectangle. This layer now
   sits still and only supplies the "camera slowly moving through the
   world" feel indirectly, by being replaced outright once the Three.js
   cubemap panorama below loads (see .ts-panorama-host and
   js/panorama.js). inset/width/height oversize it slightly as static
   overscan so its own edges are never visible even without any
   animation on top. */
.ts-bg{
  position: fixed;
  inset: -2%;
  width: 104%;
  height: 104%;
  z-index: -10;
  object-fit: cover;
  object-position: center center;
  background-color: var(--void);
}

/* ---- Background, primary layer: Three.js cubemap panorama ----
   js/panorama.js appends a <canvas> in here once the six
   assets/images/panorama/panorama_N.png faces load successfully, then
   fades it in over the video. Sits one z-index above .ts-bg so it
   visually replaces the video the instant it's ready; opacity:0 by
   default means a failed/slow cubemap load just leaves the video
   showing through, untouched -- never a black gap. */
.ts-panorama-host{
  position: fixed;
  inset: 0;
  z-index: -9;
  overflow: hidden;
  opacity: 0;
  transition: opacity 700ms var(--ease);
  pointer-events: none;
}
.ts-panorama-host.ts-panorama-visible{ opacity: 1; }
.ts-panorama-host canvas{
  display: block;
  width: 100%;
  height: 100%;
}

.ts-overlay{
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  /* The reference site's own overlay is actually rgba(0,0,0,.35) (read
     straight from its compiled CSS) -- slightly past the 0.10-0.30
     range given here. Landed at the top of that given range rather
     than matching 0.35 exactly, since it was capped explicitly; bump
     to 0.35 if you want it to match the reference precisely. */
  background: rgba(0, 0, 0, 0.28);
}

/* ---- Falling cherry-blossom petals ---- */
.ts-petals{ position: absolute; inset: 0; z-index: 3; pointer-events: none; overflow: hidden; }
.ts-petal{
  position: absolute;
  top: -10px;
  width: 8px;
  height: 8px;
  background: var(--sakura-light);
  clip-path: polygon(50% 0%, 68% 32%, 100% 50%, 68% 68%, 50% 100%, 32% 68%, 0% 50%, 32% 32%);
  opacity: 0;
  animation-name: petal-drift;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
}
.ts-petal-small{ width: 5px; height: 5px; background: #f4c9e3; }
/* Two more silhouettes for variety -- a small diamond and a plain
   square -- alongside the default 8-point petal shape. */
.ts-petal-diamond{
  clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
  background: #f0b8dc;
}
.ts-petal-square{ clip-path: none; background: #eec3de; width: 5px; height: 5px; }
@keyframes petal-drift{
  0%{ transform: translate(0, 0) rotate(0deg); opacity: 0; }
  8%{ opacity: 0.85; }
  92%{ opacity: 0.85; }
  100%{ transform: translate(70px, 108vh) rotate(320deg); opacity: 0; }
}
@keyframes petal-drift-alt{
  0%{ transform: translate(0, 0) rotate(0deg); opacity: 0; }
  8%{ opacity: 0.8; }
  92%{ opacity: 0.8; }
  100%{ transform: translate(-55px, 108vh) rotate(-280deg); opacity: 0; }
}
.ts-petal-diamond, .ts-petal-square:nth-of-type(3n){ animation-name: petal-drift-alt; }

/* ---- Top-right settings button + panel ---- */
.ts-icon-btn{
  position: absolute;
  top: calc(20 * var(--u));
  right: calc(20 * var(--u));
  z-index: 6;
  width: calc(52 * var(--u));
  height: calc(52 * var(--u));
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(180deg, #8c8c8c 0%, #8c8c8c 12%, #737373 12%, #737373 84%, #5e5e5e 84%, #5e5e5e 100%);
  border: 2px solid #1a1a1a;
  box-shadow: inset 0 2px 0 rgba(255, 255, 255, 0.3), 0 2px 0 #1a1a1a;
  color: #f2f0ea;
  cursor: pointer;
  transition: filter 120ms var(--ease), transform 80ms var(--ease);
}
.ts-icon-btn:hover, .ts-icon-btn:focus-visible{ filter: brightness(1.18); }
.ts-icon-btn:focus-visible{ outline: 2px solid var(--diamond); outline-offset: 2px; }
.ts-icon-btn:active{ filter: brightness(0.88); transform: translateY(2px); }
.ts-icon-svg{ width: calc(29 * var(--u)); height: calc(29 * var(--u)); }

.settings-panel{ position: fixed; inset: 0; z-index: 95; }
.settings-panel[hidden]{ display: none; }
.settings-backdrop{
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.6);
  border: none;
  padding: 0;
  cursor: pointer;
}
.settings-box{
  position: relative;
  z-index: 1;
  margin: 10vh auto 0;
  width: min(360px, 88vw);
  background: linear-gradient(180deg, #8c8c8c 0%, #8c8c8c 6%, #6e6e6e 6%, #6e6e6e 94%, #5a5a5a 94%, #5a5a5a 100%);
  border: 3px solid #1a1a1a;
  padding: 22px 22px 20px;
}
.settings-title{
  margin: 0 0 18px;
  font-family: var(--font-display);
  font-size: 0.9rem;
  color: var(--offwhite);
  text-shadow: 2px 2px 0 rgba(0, 0, 0, 0.5);
  text-align: center;
}
.settings-title:focus{ outline: 2px solid var(--diamond); outline-offset: 4px; }
.settings-row{
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 0;
  border-top: 1px solid rgba(0, 0, 0, 0.25);
}
.settings-row:first-of-type{ border-top: none; }
.settings-label{
  font-family: var(--font-display);
  font-size: 0.62rem;
  color: var(--offwhite);
  text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.5);
}
.settings-toggle{
  min-width: 76px;
  padding: 8px 10px;
  font-family: var(--font-display);
  font-size: 0.58rem;
  color: #ffffff;
  text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.5);
  background: linear-gradient(180deg, #6fae52 0%, #5a9640 100%);
  border: 2px solid #1a1a1a;
  cursor: pointer;
  transition: filter 120ms var(--ease), transform 80ms var(--ease);
}
.settings-toggle[aria-pressed="false"]{ background: linear-gradient(180deg, #9a9a9a 0%, #767676 100%); }
.settings-toggle:hover{ filter: brightness(1.15); }
.settings-toggle:focus-visible{ outline: 2px solid var(--diamond); outline-offset: 2px; }
.settings-toggle:active{ transform: translateY(1px); }
.settings-box .ts-back-btn{ margin: 18px 0 0; width: 100%; text-align: center; }

/* ---- Title + splash + menu ---- */
.ts-content{
  /* --logo-w is the single source of truth for the whole centre stack's
     scale -- logo, ribbon, splash and menu all derive from it, so the
     composition (matched to the layout mockup) holds at every size. The
     130vh term keeps short/wide windows from pushing the menu past the
     fold. */
  --logo-w: calc(1170 * var(--u));
  position: relative;
  z-index: 4;
  padding: 0 20px;
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* The logo PNG is 3:1 with its letters ending ~24% of its width down;
   everything below that is transparent. The negative margin pulls the
   menu up into that empty strip so it sits just under the letters. */
.ts-title-block{
  margin-bottom: calc(var(--logo-w) * -0.05);
  position: relative;
}
/* The logo is a real image now (assets/images/huy-nguyen-logo.png) --
   no letterform geometry, no extrusion stack, no filters to maintain
   here. object-fit:contain + height:auto preserve its native aspect
   ratio at any width; it is never stretched or distorted. */
.ts-title-logo{
  pointer-events: none;
  display: block;
  width: var(--logo-w);
  height: auto;
  margin: 0 auto;
  object-fit: contain;
}

/* Placed like Minecraft's splash: centred on the logo's right-hand
   edge, low on the letters, tilted ~22deg, bouncing around its centre.
   Size follows Minecraft's own rule -- scale = 1 / (6 x characters + 32)
   -- so long lines render smaller and short ones bigger, and every line
   (see SPLASHES in js/main.js, which sets --splash-len) takes up about
   the same spot on the logo's corner. z-index keeps it above the logo. */
.ts-splash{
  --splash-len: 19;
  position: absolute;
  z-index: 3;
  margin: 0;
  left: calc(var(--logo-w) * 0.9);
  top: calc(var(--logo-w) * 0.2);
  white-space: nowrap;
  text-align: center;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: calc(var(--logo-w) * 3.35 / (6 * var(--splash-len) + 32));
  /* Real Minecraft splash-text color (#FFFF55) and bounce, pulled
     straight from the reference site's compiled CSS (.splash-text,
     @keyframes splashBounce) rather than approximated: 0.5s
     ease-in-out infinite alternate between scale(1) and scale(1.12),
     with the matching dark-yellow (not black) shadow. */
  color: #ffff55;
  text-shadow: 2px 2px 0 #3f3f00;
  transform: translate(-50%, -50%) rotate(-22deg);
  transform-origin: center;
  animation: splashBounce 0.5s ease-in-out infinite alternate;
}
@keyframes splashBounce{
  0%{ transform: translate(-50%, -50%) rotate(-22deg) scale(1); }
  100%{ transform: translate(-50%, -50%) rotate(-22deg) scale(1.12); }
}

/* ~54% of the logo's width, per the mockup -- clearly narrower than
   the logo so it reads as sitting under it. The min() floor keeps the
   buttons usable on phones where 54% of the logo would be too thin. */
.ts-menu{
  /* Above .ts-title-block: the logo PNG's transparent lower strip
     overlaps the top of "About Me" (see its negative margin) and would
     otherwise swallow clicks there. */
  position: relative;
  z-index: 4;
  display: flex;
  flex-direction: column;
  gap: calc(var(--logo-w) * 0.0095);
  width: calc(var(--logo-w) * 0.535);
  min-width: 0;
}
/* A smooth, subtle gray falloff (not hard-stopped bands) plus a thick
   near-black outer border and a thin inset bevel -- reads closer to
   the reference mockup's flatter, more uniform button face than a
   strongly striped texture. Sharp square corners throughout. */
.ts-menu-btn{
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: calc(var(--logo-w) * 0.057);
  text-align: center;
  text-decoration: none;
  font-family: var(--font-display);
  font-size: calc(var(--logo-w) * 0.021);
  color: var(--offwhite);
  text-shadow: 2px 2px 0 rgba(0, 0, 0, 0.6);
  padding: calc(4 * var(--u)) calc(20 * var(--u));
  background: linear-gradient(180deg, #949494 0%, #848484 50%, #767676 100%);
  border: 3px solid #111;
  box-shadow:
    inset 2px 2px 0 rgba(255, 255, 255, 0.35),
    inset -2px -2px 0 rgba(0, 0, 0, 0.35);
  cursor: pointer;
  transition: filter 120ms var(--ease), transform 80ms var(--ease),
    background 120ms var(--ease), color 120ms var(--ease);
}
/* The real Minecraft button hover doesn't just brighten -- it shifts
   the whole button to a blue-purple tint and the label turns bright
   yellow. That color swap is one of the most recognizable interactions
   on the actual menu, so it's reproduced here (banded gradient kept,
   just re-tinted) rather than a generic brightness filter. */
.ts-menu-btn:hover, .ts-menu-btn:focus-visible{
  color: #ffff55;
  text-shadow: 2px 2px 0 #3f3f00;
  background: linear-gradient(180deg, #7e8fc4 0%, #6e7fb4 50%, #5f70a4 100%);
  box-shadow:
    inset 2px 2px 0 rgba(255, 255, 255, 0.5),
    inset -2px -2px 0 rgba(0, 0, 0, 0.4);
}
.ts-menu-btn:focus-visible{ outline: 2px solid var(--diamond); outline-offset: 3px; }
.ts-menu-btn:active{ filter: brightness(0.85); transform: translateY(2px); }

/* ---- Bottom-left: nametag, sprite, Connect-with-me, copyright ---- */
.ts-player-corner{
  position: absolute;
  bottom: calc(16 * var(--u));
  left: calc(20 * var(--u));
  z-index: 5;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  /* One spacing value for the whole column: nametag -> hair, shoes ->
     Connect, and Connect -> version line all measure --pc-gap. */
  --pc-gap: calc(14.4 * var(--u));
  gap: var(--pc-gap);
}
/* Nametag + sprite, centred as one unit over the Connect button. */
.ts-player{
  align-self: center;
  width: calc(144 * var(--u));
  display: flex;
  flex-direction: column;
  align-items: center;
  /* + the nametag tail's overhang (5u), minus the transparent frame
     above the hair (3/597 of the 313.2u sprite = 1.57u). */
  gap: calc(var(--pc-gap) + 3.43 * var(--u));
}
.ts-player .ts-sprite{ align-self: flex-start; }
.ts-nametag{
  position: relative;
  display: inline-block;
  background: rgba(15, 15, 18, 0.8);
  border: 1px solid rgba(231, 228, 218, 0.55);
  color: var(--offwhite);
  font-family: var(--font-display);
  font-size: calc(17.3 * var(--u));
  line-height: 1.3;
  padding: calc(3 * var(--u)) calc(10 * var(--u));
}
/* Small speech-bubble tail pointing down at the player's head: a
   rotated square clipped to its lower half so only the part hanging
   below the tag shows (no double-dark overlap with the tag's own
   translucent background). */
.ts-nametag::after{
  content: "";
  position: absolute;
  left: 50%;
  bottom: 0;
  width: calc(8 * var(--u));
  height: calc(8 * var(--u));
  background: inherit;
  border-right: inherit;
  border-bottom: inherit;
  transform: translate(-50%, 50%) rotate(45deg);
  clip-path: polygon(100% 0, 100% 100%, 0 100%);
}
.ts-sprite{
  /* 144x313u = the 274x597 avatar frame at 1440x900. The shoes sit
     3.15u above this box's bottom edge (6/597 transparent), so the
     negative margin keeps them exactly --pc-gap above Connect. */
  position: relative;
  width: calc(144 * var(--u));
  height: calc(313.2 * var(--u));
  margin-bottom: calc(-3.15 * var(--u));
  animation: sprite-idle 3.2s ease-in-out infinite;
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.45));
}
@keyframes sprite-idle{
  0%, 100%{ transform: translateY(0); }
  50%{ transform: translateY(calc(-5.4 * var(--u))); }
}
.pf-head{ position: absolute; top: 0; left: 24px; width: 32px; height: 32px; background: #e8b48c; transition: transform 120ms linear; }
.pf-hair{ position: absolute; top: 0; left: 0; width: 100%; height: 13px; background: #241a14; }
.pf-body{ position: absolute; top: 32px; left: 20px; width: 40px; height: 44px; background: #1c1c1c; }
.pf-shirt{ position: absolute; top: 0; left: 9px; right: 9px; bottom: 8px; background: #f2f0ea; }
.pf-arm{ position: absolute; top: 32px; width: 12px; height: 44px; background: #1c1c1c; }
.pf-arm-l{ left: 8px; }
.pf-arm-r{ left: 60px; }
.pf-leg{ position: absolute; top: 76px; width: 16px; height: 30px; background: #141414; }
.pf-leg-l{ left: 20px; }
.pf-leg-r{ left: 44px; }
.pf-shoe{ position: absolute; top: 106px; width: 16px; height: 4px; background: #f2f0ea; }
.pf-shoe-l{ left: 20px; }
.pf-shoe-r{ left: 44px; }

/* Huy's 3D-rendered Minecraft character (front view cut from his
   character sheet). Split in two images sharing one 274x597 frame so the
   head can still drift with the mouse (js/main.js targets .pf-head):
   avatar-body.png is everything but the head (neck included, so no gap
   shows when the head moves), avatar-head.png is the top 144px of the
   frame, i.e. 144/597 = 24.12% of the sprite box's height. */
.ts-avatar-body{
  position: absolute;
  inset: 0;
  background: url('../assets/images/player-skin/avatar-body.png?v=7') center/100% 100% no-repeat;
}
.ts-sprite .pf-head{
  top: 0;
  left: 0;
  width: 100%;
  height: 24.12%;
  background: url('../assets/images/player-skin/avatar-head.png?v=7') center/100% 100% no-repeat;
}
/* 3D skin render (js/player.js) replaces the flat fallback images. */
.ts-sprite-canvas{ position: absolute; inset: 0; display: block; width: 100%; height: 100%; }
.ts-sprite-3d .ts-avatar-body,
.ts-sprite-3d .pf-head{ display: none; }

/* Compact Minecraft button sized to sit just under the player, with the
   player group centred over it. */
.ts-linkedin-btn{
  display: flex;
  align-items: center;
  justify-content: center;
  gap: calc(8.6 * var(--u));
  width: calc(184 * var(--u));
  background: linear-gradient(180deg, #8c8c8c 0%, #8c8c8c 12%, #737373 12%, #737373 84%, #5e5e5e 84%, #5e5e5e 100%);
  border: 2px solid #1a1a1a;
  box-shadow: inset 0 2px 0 rgba(255, 255, 255, 0.3), inset 0 -2px 0 rgba(0, 0, 0, 0.3);
  color: var(--offwhite);
  text-decoration: none;
  font-family: var(--font-display);
  font-size: calc(14.4 * var(--u));
  white-space: nowrap;
  text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.5);
  padding: calc(10 * var(--u)) calc(14 * var(--u));
  transition: filter 120ms var(--ease), transform 80ms var(--ease);
}
.ts-linkedin-btn:hover{ filter: brightness(1.15); }
.ts-linkedin-btn:focus-visible{ outline: 2px solid var(--diamond); outline-offset: 2px; }
.ts-linkedin-btn:active{ filter: brightness(0.85); transform: translateY(2px); }
.ts-linkedin-icon-box{
  display: flex;
  align-items: center;
  justify-content: center;
  width: calc(27.4 * var(--u));
  height: calc(27.4 * var(--u));
  background: #0a66c2;
  flex-shrink: 0;
}
.ts-linkedin-icon{ width: 70%; height: 70%; fill: #ffffff; }

/* Flows inline within .ts-player-corner's stack -- bottom-left, next to
   the version's real-Minecraft home. */
.ts-corner-version{
  margin: 0;
  font-family: var(--font-display);
  font-size: calc(8.8 * var(--u));
  color: rgba(255, 255, 255, 0.85);
  text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.6);
}

/* ---- Bottom-right: copyright, opposite corner ---- */
.ts-corner-copyright{
  position: absolute;
  bottom: calc(16 * var(--u));
  right: calc(20 * var(--u));
  z-index: 5;
  margin: 0;
  font-family: var(--font-display);
  font-size: calc(8.8 * var(--u));
  color: rgba(255, 255, 255, 0.6);
  text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.6);
}
.ts-corner-copyright{ display: flex; flex-direction: column; align-items: flex-end; gap: calc(4 * var(--u)); text-align: right; }
.ts-credit a{ color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.ts-credit a:hover{ color: #ffffff; }
.ts-credit a:focus-visible{ outline: 2px solid var(--diamond); outline-offset: 2px; }

/* ---- Menu windows (About / Experience / Projects) ----
   A modal window over the dimmed titlescreen: grey-bordered frame, a
   "[ TITLE ]" header bar with a Close button, and a near-black body.
   Everything inside uses the pixel display font. */
.view-panel{
  position: fixed;
  inset: 0;
  z-index: 70;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(12px, 4vh, 48px) clamp(10px, 3vw, 40px);
  opacity: 0;
  transition: opacity 220ms var(--ease);
}
.view-panel[hidden]{ display: none; }
.view-panel.panel-visible{ opacity: 1; }
.mw-backdrop{
  position: absolute;
  inset: 0;
  border: none;
  padding: 0;
  background: rgba(0, 0, 0, 0.78);
  cursor: pointer;
}
.mw{
  position: relative;
  width: min(1120px, 100%);
  max-height: 100%;
  display: flex;
  flex-direction: column;
  background: #111113;
  border: 3px solid #5a5a5a;
  box-shadow: 0 0 0 3px #1a1a1a, 0 20px 60px rgba(0, 0, 0, 0.6);
  font-family: var(--font-display);
  color: #f2f0ea;
  transform: translateY(10px);
  transition: transform 220ms var(--ease);
}
.panel-visible .mw{ transform: none; }
.mw-head{
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: clamp(14px, 2.2vh, 22px) clamp(16px, 2.4vw, 32px);
  background: #2c2c2e;
  border-bottom: 3px solid #5a5a5a;
}
.mw-title{
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.05rem, 2vw, 1.55rem);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #ffffff;
  text-shadow: 2px 2px 0 rgba(0, 0, 0, 0.5);
}
.mw-title:focus{ outline: none; }
.mw-close{
  flex-shrink: 0;
  background: none;
  border: none;
  padding: 4px 2px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(0.85rem, 1.5vw, 1.2rem);
  color: #b9b9b9;
  cursor: pointer;
  transition: color 120ms var(--ease);
}
.mw-close span{ font-size: 0.8em; margin-right: 6px; }
.mw-close:hover, .mw-close:focus-visible{ color: #ff5f5f; }
.mw-close:focus-visible{ outline: 2px solid var(--diamond); outline-offset: 3px; }
.mw-body{
  overflow-y: auto;
  padding: clamp(18px, 3vh, 34px) clamp(16px, 2.4vw, 34px);
}

.mw-kicker{
  margin: 0 0 10px;
  font-size: 0.9rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #ffd84a;
}
.mw-kicker:not(:first-child){ margin-top: 30px; }
.mw-text{ margin: 0; font-size: 0.95rem; line-height: 1.7; }
.mw-strong{ margin: 0 0 4px; font-size: 1rem; font-weight: 700; }
.mw-muted{ margin: 0; font-size: 0.85rem; line-height: 1.7; color: #a3a3a3; }
.mw-hint{ margin: 0 0 20px; font-size: 0.9rem; color: #c9c9c9; }

/* Same grey Minecraft button face as the title menu, full width. */
.mw-wide-btn{
  display: block;
  margin-top: 34px;
  padding: 14px 16px;
  text-align: center;
  text-decoration: none;
  font-weight: 700;
  font-size: 1rem;
  color: var(--offwhite);
  text-shadow: 2px 2px 0 rgba(0, 0, 0, 0.6);
  background: linear-gradient(180deg, #949494 0%, #848484 50%, #767676 100%);
  border: 3px solid #111;
  box-shadow: inset 2px 2px 0 rgba(255, 255, 255, 0.35), inset -2px -2px 0 rgba(0, 0, 0, 0.35);
  transition: background 120ms var(--ease), color 120ms var(--ease);
}
.mw-wide-btn:hover, .mw-wide-btn:focus-visible{
  color: #ffff55;
  text-shadow: 2px 2px 0 #3f3f00;
  background: linear-gradient(180deg, #7e8fc4 0%, #6e7fb4 50%, #5f70a4 100%);
}
.mw-wide-btn:focus-visible{ outline: 2px solid var(--diamond); outline-offset: 3px; }

.bio-link{ color: var(--diamond); text-decoration: none; }
.bio-link:hover{ text-decoration: underline; }
.bio-link:focus-visible{ outline: 2px solid var(--diamond); outline-offset: 2px; }

/* ---- About: photo carousel + player info ---- */
.about-grid{
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
  gap: clamp(24px, 4vw, 56px);
  align-items: start;
}
.about-frame{
  position: relative;
  aspect-ratio: 3 / 4;
  border: 3px solid #5a5a5a;
  background: #1b1b1d;
  overflow: hidden;
}
.about-slide{
  position: absolute;
  inset: 0;
  margin: 0;
  display: none;
}
.about-slide.is-active{ display: block; }
.about-slide > img{ width: 100%; height: 100%; object-fit: cover; }
.about-slide figcaption{
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 12px 16px;
  background: rgba(22, 22, 24, 0.92);
  border-top: 2px solid #3c3c3c;
  font-size: 0.9rem;
  font-weight: 700;
}
.about-nav{
  position: absolute;
  top: 50%;
  width: 44px;
  height: 44px;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-size: 1.5rem;
  line-height: 1;
  color: #ffffff;
  background: rgba(30, 30, 32, 0.85);
  border: 2px solid #6a6a6a;
  cursor: pointer;
}
.about-nav:hover{ background: #3a3a3e; }
.about-nav:focus-visible{ outline: 2px solid var(--diamond); outline-offset: 2px; }
.about-prev{ left: 8px; }
.about-next{ right: 8px; }
.about-dots{ display: flex; justify-content: center; gap: 10px; margin-top: 16px; }
.about-dots span{ width: 9px; height: 9px; background: #555; }
.about-dots span.is-active{ background: #62d34f; }
.about-count{ margin: 12px 0 0; text-align: center; font-size: 0.78rem; color: #8d8d8d; }
.is-single .about-nav, .is-single .about-count{ display: none; }

.about-name{ margin: 0 0 12px; font-size: clamp(1.3rem, 2.2vw, 1.75rem); font-weight: 700; }
/* Compact About window: smaller type and tighter rhythm so the whole
   panel fits a 1440x900 screen without scrolling. */
#panel-about .mw-kicker{ font-size: 0.78rem; margin-bottom: 8px; }
#panel-about .mw-kicker:not(:first-child){ margin-top: 20px; }
#panel-about .about-name{ margin-bottom: 8px; font-size: clamp(1.15rem, 1.8vw, 1.45rem); }
#panel-about .mw-text{ font-size: 0.85rem; line-height: 1.6; }
#panel-about .mw-strong{ font-size: 0.9rem; }
#panel-about .mw-muted{ font-size: 0.78rem; line-height: 1.6; }
#panel-about .skill-chips{ gap: 7px; }
#panel-about .skill-chips li{ padding: 4px 9px; font-size: 0.7rem; border-width: 2px; }
#panel-about .mw-wide-btn{ margin-top: 24px; padding: 11px 14px; font-size: 0.9rem; }
#panel-about .about-grid{ gap: clamp(24px, 3.4vw, 48px); }
/* Picture column stretches to the info column's full height at every
   zoom level, so the photo always lines up top and bottom with the text
   (dots sit under it). */
#panel-about .about-grid{ align-items: stretch; }
#panel-about .about-gallery{ display: flex; flex-direction: column; }
#panel-about .about-frame{ flex: 1; aspect-ratio: auto; min-height: 320px; }
.about-contact{ margin-top: 10px; }
.skill-chips{ list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 10px; }
.skill-chips li{
  padding: 6px 12px;
  font-size: 0.8rem;
  font-weight: 700;
  background: #2b2b2d;
  border: 2px solid #585858;
  box-shadow: inset 0 -2px 0 rgba(0, 0, 0, 0.35);
}

/* ---- Experience: 3D flip cards with a coloured left edge ----
   Front: company, role, dates, place. Hover / keyboard focus / tap turns
   the card over (rotateY) to the details on the back. */
.exp-grid{
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(16px, 2vw, 28px);
}
.exp-card{
  perspective: 1400px;
  cursor: pointer;
}
.exp-card:focus-visible{ outline: 2px solid var(--diamond); outline-offset: 3px; }
/* Both faces sit in the same grid cell, so the card is always as tall
   as its longer side -- the back's description never spills out, however
   narrow the window gets. */
.exp-inner{
  position: relative;
  display: grid;
  min-height: clamp(220px, 32vh, 320px);
  height: 100%;
  transform-style: preserve-3d;
  transition: transform 600ms cubic-bezier(0.4, 0.2, 0.2, 1);
}
.exp-card:hover .exp-inner,
.exp-card:focus-visible .exp-inner,
.exp-card.is-open .exp-inner{ transform: rotateY(180deg); }
.exp-face{
  grid-area: 1 / 1;
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 24px 28px;
  text-align: center;
  background: #1c1c1e;
  border: 2px solid #3a3a3c;
  border-left: 6px solid var(--accent);
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
}
/* The back reads as the card turned over: the accent edge sits on the
   right, like the reference. */
.exp-back{
  transform: rotateY(180deg);
  background: #1c1c1e;
  align-items: flex-start;
  text-align: left;
  border-left: 2px solid #3a3a3c;
  border-right: 6px solid var(--accent);
}
.exp-org{ margin: 0 0 14px; font-size: clamp(1.1rem, 2vw, 1.55rem); font-weight: 700; }
.exp-role{ margin: 0 0 14px; font-size: clamp(0.85rem, 1.3vw, 1.05rem); font-weight: 700; color: var(--accent); }
.exp-dates{ margin: 0 0 10px; font-size: 0.9rem; color: #bdbdbd; }
.exp-place{ margin: 0 0 16px; font-size: 0.8rem; color: #8a8a8a; }
.exp-more{ margin: 0; font-size: 0.7rem; color: #6f6f6f; }
.exp-back-title{
  margin: 0 0 12px;
  font-size: 0.9rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent);
}
.exp-back p{ margin: 0; font-size: 0.9rem; font-weight: 700; line-height: 1.65; color: #ffffff; }
.exp-tags{ list-style: none; margin: 18px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 8px; }
.exp-tags li{
  padding: 4px 10px;
  font-size: 0.72rem;
  font-weight: 700;
  background: #2b2b2d;
  border: 2px solid #585858;
  box-shadow: inset 0 -2px 0 rgba(0, 0, 0, 0.35);
}
/* Reduce Motion: still turns over, just without the spin. */
html.motion-off .exp-inner{ transition: none; }

/* ---- Projects: stacked cards with a thick coloured left edge ---- */
.proj-list{ display: flex; flex-direction: column; gap: clamp(18px, 2.6vh, 30px); }
.proj-card{
  padding: clamp(18px, 2.8vh, 30px) clamp(20px, 2.6vw, 36px);
  background: #1a1a1c;
  border: 2px solid #3a3a3c;
  border-left: 6px solid var(--accent);
}
/* Title on the left, dates (when given) on the right. */
.proj-head{
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin: 0 0 14px;
}
.proj-title{
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0;
  font-size: clamp(1rem, 1.7vw, 1.3rem);
  font-weight: 700;
}
.proj-date{
  flex-shrink: 0;
  padding-top: 4px;
  font-size: 0.8rem;
  font-weight: 700;
  color: #9a9a9a;
  white-space: nowrap;
}
.proj-icon{ font-size: 1em; }
.proj-tags{ list-style: none; margin: 0 0 16px; padding: 0; display: flex; flex-wrap: wrap; gap: 8px; }
.proj-tags li{
  padding: 3px 9px;
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--accent);
  border: 2px solid var(--accent);
}
.proj-points{ list-style: none; margin: 0 0 16px; padding: 0; }
.proj-points li{
  position: relative;
  padding-left: 22px;
  margin-bottom: 10px;
  font-size: 0.88rem;
  font-weight: 700;
  line-height: 1.7;
  color: #f0f0f0;
}
.proj-points li::before{
  content: "\25B8";
  position: absolute;
  left: 0;
  color: var(--accent);
}

/* Back button -- used by the Settings panel. */
.ts-back-btn{
  display: inline-block;
  margin-bottom: 28px;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: 0.82rem;
  color: var(--offwhite);
  padding: 9px 16px;
  background: linear-gradient(180deg, #6a6a6a 0%, #59595a 45%, #4f4f4f 60%, #464646 100%);
  border: 2px solid #2e2e2e;
  border-top-color: #7d7d7d;
  border-left-color: #7d7d7d;
  box-shadow: 0 2px 0 #2e2e2e;
  cursor: pointer;
  transition: filter 120ms var(--ease), box-shadow 120ms var(--ease), transform 80ms var(--ease);
}
.ts-back-btn:hover, .ts-back-btn:focus-visible{
  filter: brightness(1.18);
  box-shadow: 0 2px 0 #2e2e2e, 0 0 12px 1px var(--diamond);
}
.ts-back-btn:focus-visible{ outline: 2px solid var(--diamond); outline-offset: 2px; }
.ts-back-btn:active{ transform: translateY(2px); box-shadow: 0 0 0 #2e2e2e; }

/* ---- Achievement toasts ---- */
.toast-container{
  position: fixed;
  top: 16px;
  right: 16px;
  z-index: 90;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 10px;
  pointer-events: none;
}
.achievement-toast{
  background: #1a1a1c;
  border: 3px solid #5a5a5a;
  box-shadow: inset 0 0 0 2px #0e0e0f, 0 6px 18px rgba(0, 0, 0, 0.5);
  padding: 10px 18px 10px 12px;
  font-family: var(--font-display);
  display: flex;
  align-items: center;
  gap: 10px;
  width: 310px;
  max-width: calc(100vw - 32px);
  transform: translateX(calc(100% + 24px));
  opacity: 0;
  transition: transform 400ms var(--ease), opacity 400ms var(--ease);
}
.achievement-toast.show{ transform: translateX(0); opacity: 1; }
.achievement-icon{
  width: 32px;
  height: 32px;
  background: var(--gold);
  flex-shrink: 0;
  position: relative;
}
.achievement-icon::after{
  content: "\2605";
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--void);
  font-size: 1rem;
}
.achievement-text{ display: flex; flex-direction: column; gap: 2px; }
.achievement-label{ font-size: 0.68rem; font-weight: 700; color: #ffd84a; }
.achievement-title{ font-size: 0.85rem; color: #ffffff; font-weight: 700; }

/* ---- Small screens ---- */
@media (max-width: 640px){
  .about-grid, .exp-grid{ grid-template-columns: minmax(0, 1fr); }
  .about-gallery{ max-width: 320px; margin: 0 auto; width: 100%; }
  #panel-about .about-frame{ flex: none; aspect-ratio: 4 / 5; min-height: 0; }
  .loading-logo{ padding: 24px 32px; }
  /* .ts-menu / .ts-title-logo / .ts-title-block no longer need an
     override here -- --logo-w and the menu's own clamp()s already
     scale down cohesively below 640px. */
}
