/* =============================================================
   Mali Land-Strykowski — academic site
   Liquid-glass tiles over a photographic background.
   ============================================================= */

:root{
  --text: rgba(255,255,255,1.0);
  --text-dim: rgba(255,255,255,0.72);
  --text-faint: rgba(255,255,255,0.55);

  /* Bevel edge colours. */
  --edge: rgba(255,255,255,0.22);
  --edge-soft: rgba(255,255,255,0.10);

  /* Glass interior tint — kept translucent and slightly darker than the
     background photograph, so tiles never read as "whiter than the wall". */
  --glass-tint: rgba(20,26,40,0.28);
  --glass-tint-strong: rgba(28,36,56,0.42);

  --shadow: 0 30px 60px -18px rgba(0,0,0,0.55), 0 8px 24px -12px rgba(0,0,0,0.45);
  --radius: 22px;
  --radius-lg: 26px;
  --radius-sm: 16px;
  --gap: 22px;

  --sans: "Inter", -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Helvetica, Arial, sans-serif;
  --mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  /* Bevel geometry — the ::before ring thickness. */
  --bevel: 1px;

  /* Background dimming — off by default. */
  --bg-darken: 0;
}

*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; height: 100%; }
html { -webkit-text-size-adjust: 100%; overflow-x: hidden; }
body{
  font-family: var(--sans);
  color: var(--text);
  background: #0b0d10;
  min-height: 100vh;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
body.scroll-locked { overflow: hidden; touch-action: none; }

/* ---------- Background ---------- */
/* Position fixed with 100lvh so the layer always covers the *largest*
   viewport height. On iOS Safari the visible viewport height changes as the
   URL bar hides/reveals; using `100vh` (or `inset:0` in some engines) causes
   the background to rescale, leaving black bars at top/bottom. `lvh` locks
   the size to the max, so scrolling never triggers a re-cover. `translateZ`
   forces a compositor layer to stop iOS from re-rasterising on scroll. */
#bg {
  position: fixed;
  top: 0; left: 0;
  width: 100vw;
  height: 100vh;
  height: 100lvh;
  z-index: -2;
  overflow: hidden;
  transform: translateZ(0);
  -webkit-transform: translateZ(0);
}
/* Child image layers fill the fixed #bg parent exactly (inset:0), matching the
   WebGL lensing canvas's cover-fit. Any mismatch here shows up as a zoom pop
   when the shader canvas fades out at the end of a transition. */
#bg-img{
  position: absolute; inset: 0;
  background-image: var(--bg-url);
  background-size: cover;
  background-position: center;
  will-change: opacity;
  transition: opacity .5s ease;
}
/* Second layer used to cross-fade in higher-res background variants. */
#bg-img-hi{
  position: absolute; inset: 0;
  background-image: var(--bg-url-hi);
  background-size: cover;
  background-position: center;
  opacity: 0;
  transition: opacity .6s ease;
  will-change: opacity;
}
#bg-img-hi.ready{ opacity: 1; }
#bg-vignette{
  position: absolute; inset: 0;
  background: rgba(0,0,0,1);
  opacity: var(--bg-darken);
  pointer-events: none;
}

/* ---------- Layout ---------- */
main#app{
  position: relative;
  z-index: 1;
  max-width: 900px;
  margin: 0 auto;
  /* Bottom padding accounts for the iOS home-indicator safe area so the last
     tile never sits flush against Safari's URL bar or the home indicator. */
  padding:
    clamp(28px, 6vw, 64px)
    clamp(18px, 4vw, 32px)
    calc(120px + env(safe-area-inset-bottom, 0px));
  display: flex;
  flex-direction: column;
  gap: var(--gap);
}

.row{ display: flex; gap: var(--gap); flex-wrap: wrap; }
.row > *{ flex: 1 1 0; min-width: 0; }
.row-short{ margin-top: 0; }
/* Narrow row: children size to their content, remainder is left blank. */
.row-narrow{ flex-wrap: nowrap; align-items: stretch; }
.row-narrow > *{ flex: 0 0 auto; min-width: 0; }
.row-narrow > .stretch{ flex: 1 1 auto; min-width: 0; }

/* ---------- The glass tile ---------- */
.tile{
  position: relative;
  border-radius: var(--radius);
  padding: 22px 24px;
  color: var(--text);
  background:
    linear-gradient(140deg,
      rgba(255,255,255,0.10),
      rgba(255,255,255,0.02) 55%,
      rgba(255,255,255,0.07)
    ),
    var(--glass-tint);
  border: 1px solid var(--edge);
  box-shadow: var(--shadow);
  backdrop-filter: blur(28px) saturate(180%);
  -webkit-backdrop-filter: blur(28px) saturate(180%);
  overflow: hidden;
  isolation: isolate;
  transition: transform .35s cubic-bezier(.2,.7,.2,1), box-shadow .35s ease, background .3s ease;
}

/* Thin refractive bevel ring around the edge. */
.tile::before{
  content: "";
  position: absolute; inset: 0;
  border-radius: inherit;
  padding: var(--bevel);
  background:
    linear-gradient(140deg,
      rgba(255,255,255,0.85) 0%,
      rgba(180,220,255,0.45) 12%,
      rgba(255,255,255,0.12) 30%,
      rgba(255,220,240,0.08) 52%,
      rgba(255,255,255,0.10) 68%,
      rgba(180,240,255,0.45) 88%,
      rgba(255,255,255,0.85) 100%
    );
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
  opacity: 1;
  mix-blend-mode: screen;
}

/* Inside-the-bevel chromatic wash. */
.tile::after{
  content: "";
  position: absolute; inset: 0;
  border-radius: inherit;
  background:
    radial-gradient(120% 80% at 0% 0%, rgba(150,200,255,0.18), transparent 45%),
    radial-gradient(120% 80% at 100% 100%, rgba(255,180,215,0.14), transparent 45%);
  mix-blend-mode: screen;
  pointer-events: none;
  opacity: .8;
  z-index: -1;
}

/* Interactive tile (button-like). */
.tile.button{
  cursor: pointer;
  padding: 16px 20px;
  display: flex;
  align-items: center;
  gap: 12px;
  font-weight: 500;
  font-size: 15px;
  text-align: left;
  color: var(--text);
  font-family: inherit;
  width: 100%;
}
.tile.button > svg.icon,
.tile.button > img.icon{
  width: 18px; height: 18px;
  flex-shrink: 0;
  color: var(--text);
}
.tile.button > span{ flex: 1; }
.tile.button > span.arrow-glyph{
  flex: 0 0 auto;
  margin-left: auto;
  opacity: .9;
  font-size: 17px;
  line-height: 1;
}
.tile.button:hover{
  transform: translateY(-1px);
  background:
    linear-gradient(140deg,
      rgba(255,255,255,0.14),
      rgba(255,255,255,0.03) 55%,
      rgba(255,255,255,0.09)
    ),
    var(--glass-tint-strong);
}
.tile.button:active{ transform: translateY(0); }

/* Short tile — used for section headers, See-all buttons, back button, subview title. */
.tile.short-tile{
  padding: 12px 18px;
  border-radius: var(--radius-sm);
  min-height: 48px;
  display: flex;
  align-items: center;
}
.tile.short-tile.button{ padding: 12px 18px; }

/* Sizes fit to content — no filling. */
.tile.fit{
  flex: 0 0 auto;
  width: -moz-fit-content;
  width: fit-content;
}

.section-tile{
  color: var(--text);
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  min-width: 0;
}
.section-tile .section-label-text{
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  display: block;
}
.section-see-all{ font-size: 14px; font-weight: 500; }
.big-button{ font-size: 15px; font-weight: 600; }
.big-button-label-short{ display: none; }
@media (max-width: 620px){
  .big-button-label-full{ display: none; }
  .big-button-label-short{ display: inline; }
}

/* Mobile: let the section header take remaining width and the see-all
   button be its intrinsic size so the pair fits the viewport exactly. Also
   tighten the label so long titles fit at the reduced width. */
@media (max-width: 620px){
  .row-narrow > .section-tile{ flex: 1 1 0; min-width: 0; width: auto; }
  .row-narrow > .section-see-all{ flex: 0 0 auto; }
  .section-tile{ font-size: 10.5px; letter-spacing: 0.14em; }
  .tile.short-tile,
  .tile.short-tile.button{ padding: 10px 14px; }
}

/* ---------- Header (name + headshot) ---------- */
.header-row{
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--gap);
  align-items: stretch;
}
.name-tile{
  display: flex; flex-direction: column; justify-content: center;
  gap: 18px;
  padding: 28px 30px;
  overflow: hidden;
}
.name-h1{
  margin: 0;
  font-family: var(--sans);
  font-weight: 400;
  line-height: 1.02;
  letter-spacing: -0.02em;
  color: var(--text);

  display: inline-block;
  white-space: nowrap;
  max-width: 100%;
  font-size: 60px; /* JS overrides on mount */
}
.name-sub{
  font-family: var(--sans);
  font-weight: 500;
  color: var(--text-dim);
  letter-spacing: 0.005em;
  line-height: 1.15;

  display: inline-block;
  white-space: nowrap;
  max-width: 100%;
  font-size: 18px; /* JS overrides on mount */
}
/* Mobile-only two-line subtitle. Hidden on desktop; the autofit `.name-sub`
   is hidden on mobile (see .header-row media query below). */
.name-sub-mobile{ display: none; }

.headshot-tile{
  width: clamp(120px, 18vw, 168px);
  aspect-ratio: 1 / 1;
  padding: 8px;
  border-radius: var(--radius-lg);
}
.headshot-tile .frame{
  width: 100%; height: 100%;
  border-radius: calc(var(--radius-lg) - 10px);
  background-image: var(--headshot-url);
  background-size: cover;
  background-position: center;
  box-shadow: inset 0 0 0 1px rgba(255,255,255,0.22);
}

@media (max-width: 620px){
  /* Keep the desktop ordering — name-tile on the left, headshot on the right. */
  .header-row{ grid-template-columns: 1fr auto; }
  .name-tile{ padding: 18px 20px; gap: 12px; }
  /* Explicit height matches width so the tile stays square regardless of the
     name-tile's height (default grid stretch would otherwise turn it into a
     rectangle when the name-tile grows). */
  .headshot-tile{
    width:  clamp(88px, 24vw, 120px);
    height: clamp(88px, 24vw, 120px);
    align-self: start;
  }

  /* Hide the autofit single-line subtitle; show the two-line mobile version. */
  .name-sub{ display: none; }
  .name-sub-mobile{
    display: block;
    font-family: var(--sans);
    font-weight: 500;
    font-size: 13.5px;
    line-height: 1.3;
    color: var(--text-dim);
    letter-spacing: 0.005em;
  }
}

/* ---------- About ---------- */
.about-tile p{
  margin: 0 0 12px;
  font-family: var(--sans);
  font-weight: 400;
  font-size: 16px;
  line-height: 1.55;
  color: var(--text);
}
.about-tile p:last-child{ margin-bottom: 0; }

/* ---------- Publication tile ---------- */
.pub-tile{ padding: 20px 24px 18px; }
/* Render as a normal inline text flow (not a flex row), so wrapping happens
   at word boundaries within the string rather than being forced between
   dotted segments. */
.pub-sub{
  margin: 0 0 6px;
  font-family: var(--sans);
  font-size: 13.5px;
  color: var(--text-dim);
  line-height: 1.4;
}
.pub-sub .seg{ display: inline; }
.pub-sub .sep{ opacity: .55; padding: 0 6px; white-space: nowrap; }
.pub-tile h3{
  margin: 0 0 12px;
  font-family: var(--sans);
  font-weight: 600;
  font-size: 19px;
  line-height: 1.3;
  letter-spacing: -0.005em;
  color: var(--text);
}
.pub-links{
  display: flex; align-items: center;
  gap: 12px;
  margin-top: 6px;
  padding-top: 12px;
  border-top: 1px solid var(--edge-soft);
  min-height: 26px;
}
.pub-links > :first-child{ margin-right: auto; }
.pub-links > :last-child:not(:only-child) { margin-left: auto; }
.pub-links > :only-child { margin-right: auto; }
.pub-status{
  color: var(--text);
  font-family: var(--sans);
  font-size: 13.5px;
  letter-spacing: 0.02em;
}

/* Linked tile titles. Sit flush in the heading — same colour and weight as an
   unlinked title, revealing themselves as links only on hover. */
.title-link{
  color: inherit;
  text-decoration: none;
  transition: color .2s;
}
.title-link:hover{ color: #fff; text-decoration: underline; text-underline-offset: 3px; }

.textlink{
  display: inline-flex; align-items: center; gap: 8px;
  color: var(--text);
  text-decoration: none;
  font-size: 13px;
  font-family: var(--mono);
  letter-spacing: 0.01em;
  padding: 4px 6px;
  border-radius: 6px;
  transition: color .2s, opacity .2s, background .2s;
}
.textlink .icon{ width: 16px; height: 16px; flex-shrink: 0; object-fit: contain; }
.textlink:hover{ color: #fff; text-decoration: underline; text-underline-offset: 3px; }

img.icon{ object-fit: contain; display: inline-block; }
.textlink img.icon.arxiv-icon{ width: 14px; height: 20px; }

/* ---------- Entry tile (talks / posters / prizes) ---------- */
.entry-tile{ padding: 18px 22px 18px; }
.entry-tile .entry-head{
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
}
.entry-tile .chip{
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  border: 1px solid var(--edge);
  color: var(--text);
  background: rgba(255,255,255,0.06);
  white-space: nowrap;
  flex: 0 0 auto;
  align-self: center;
}
.entry-tile h3{
  margin: 0;
  font-family: var(--sans);
  font-weight: 600;
  font-size: 19px;
  line-height: 1.3;
  letter-spacing: -0.005em;
  color: var(--text);
  flex: 1 1 auto;
  min-width: 0;
}
.entry-tile .entry-meta{
  color: var(--text-dim);
  font-size: 13.5px;
  line-height: 1.5;
  margin-top: 6px;
}
.entry-tile .entry-meta .seg{ display: inline; }
.entry-tile .entry-meta .sep{ opacity: .55; padding: 0 6px; white-space: nowrap; }
/* Slides / Preview / Recording read as plain clickable text — no underline;
   the pointer cue is enough. Same look as the surrounding meta text. */
.entry-tile .entry-meta a.textlink,
.entry-tile .entry-meta a.meta-link{
  padding: 0;
  font-family: var(--sans);
  font-size: 13.5px;
  color: var(--text-dim);
  border-radius: 0;
  background: none;
  border: none;
  text-decoration: none;
}
.entry-tile .entry-meta a.textlink:hover,
.entry-tile .entry-meta a.meta-link:hover{
  background: none;
  color: #fff;
  text-decoration: none;
}
.entry-tile .entry-note{
  margin: 6px 0 0;
  color: var(--text-dim);
  font-size: 13.5px;
  line-height: 1.5;
}

/* ---------- Contact ---------- */
.contact-tile{ padding: 20px 22px; }
.contact-grid{
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px 14px;
}
@media (max-width: 620px){
  .contact-grid{ grid-template-columns: repeat(2, 1fr); }
}
.contact-cell{
  display: inline-flex; align-items: center; gap: 12px;
  color: var(--text);
  text-decoration: none;
  padding: 12px 14px;
  border-radius: 12px;
  background: rgba(255,255,255,0.04);
  border: 1px solid var(--edge-soft);
  transition: background .2s, transform .2s;
  min-width: 0;
}
.contact-cell:hover{ background: rgba(255,255,255,0.12); transform: translateY(-1px); }
.contact-cell .icon{ width: 20px; height: 20px; flex-shrink: 0; color: var(--text); }
.contact-cell img.icon{ object-fit: contain; }
.contact-cell img.icon.arxiv-icon{ width: 18px; height: 26px; }
.contact-cell img.icon.ads-icon{ width: 24px; height: 24px; }
.contact-cell .contact-label{
  font-family: var(--sans);
  font-size: 14.5px;
  font-weight: 500;
  letter-spacing: 0.01em;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ---------- Sub-view header ---------- */
.subview-header-row{ align-items: stretch; }
.subview-header-row .back-button-tile{ padding: 12px 20px; }
.subview-header-row .back-button-tile > svg.icon{ width: 16px; height: 16px; }
.subview-header-row .subview-title-tile{ padding: 12px 20px; }
.subview-title{
  margin: 0;
  font-family: var(--sans);
  font-weight: 400;
  letter-spacing: -0.005em;
  color: var(--text);
  display: inline-block;
  white-space: nowrap;
  max-width: 100%;
  font-size: 15px;
  line-height: 1.15;
}
/* On mobile, show the shorter section words ("Papers", "Prizes", ...) so the
   title fits inside the tile next to the Home button. */
.subview-title .subview-title-short{ display: none; }
@media (max-width: 620px){
  .subview-title .subview-title-full{ display: none; }
  .subview-title .subview-title-short{ display: inline; }
  .subview-title{ font-size: 13.5px; }
}

/* ---------- FX canvas ---------- */
/* Layering during the two transition phases:
     forward — canvas ABOVE tiles (tiles look consumed by the horizon);
     reverse — tiles ABOVE canvas (tiles emerge in front of the horizon). */
#fx-canvas{
  position: fixed; inset: 0;
  width: 100%; height: 100%;
  pointer-events: none;
  z-index: 5;
  opacity: 0;
  transition: opacity 120ms linear;
}
#fx-canvas.active{ opacity: 1; }

main#app.in-transition{ pointer-events: none; }
main#app.in-transition-forward{ z-index: 1; }
main#app.in-transition-reverse{ z-index: 10; }
main#app.in-transition .tile{
  will-change: transform, opacity;
  transition: none;
  transform-origin: 50% 50%;
}

/* ---------- Reduced motion ---------- */
@media (prefers-reduced-motion: reduce){
  .tile, .tile.button, main#app.in-transition .tile{ transition: none !important; }
  #fx-canvas{ display: none; }
}
