:root{
  --void:#0A0D13;
  --deep:#111621;
  --card:#161C29;
  --line:#242D3E;
  --ink:#E8EBF1;
  --mute:#78849A;
  --violet:#7000FF;
  --violet-dim:#45109C;
  --violet-lite:#A46BFF;
  --danger:#FF5470;

  --serif:"Lora", Georgia, "PT Serif", "Times New Roman", serif;
  --sans:"IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --mono:"IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;
  --h:60px;
}

*{box-sizing:border-box;}
html,body{height:100%;}
body{
  margin:0;background:var(--void);color:var(--ink);font-family:var(--sans);
  -webkit-font-smoothing:antialiased;overflow:hidden;overscroll-behavior:none;
}
button{font:inherit;color:inherit;background:none;border:none;cursor:pointer;}
input,select,textarea{font:inherit;}
:focus-visible{outline:2px solid var(--violet-lite);outline-offset:2px;}
a{color:var(--violet-lite);}

.mono{font-family:var(--mono);font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:var(--mute);}
.spacer{flex:1 1 auto;}
[hidden]{display:none !important;}

/* ---------- header ---------- */
header{
  position:fixed;inset:0 0 auto 0;height:var(--h);z-index:60;
  display:flex;align-items:center;gap:16px;padding:0 18px;
  background:rgba(10,13,19,.82);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);
  border-bottom:1px solid var(--line);
}
.brand{display:inline-block;padding:0;text-decoration:none;transition:color .18s;}
.brand b{font-family:var(--mono);font-weight:500;font-size:12px;letter-spacing:.26em;}
.brand:hover{color:var(--violet-lite);}
.crumb{display:flex;align-items:center;gap:10px;min-width:0;}
.crumb .sep{color:var(--violet-dim);}
.crumb .name,
#crumbName{
  font-family:var(--serif);font-size:17px;line-height:1.2;padding:0;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;transition:color .18s;
}
#crumbName:hover{color:var(--violet-lite);}

/* header controls collapse to icons: on a phone the breadcrumb used to be
   the only way home, and it was off-screen behind the album title */
/* Centred without flexbox on purpose: iOS Safari mishandles flex layout
   inside <button>, and the SVG child collapses to nothing — the icons were
   invisible on the phone while the same markup rendered fine on desktop.
   The zero-width ::before plus vertical-align works everywhere. */
.iconbtn{
  width:32px;height:32px;flex:0 0 auto;padding:0;
  display:inline-block;text-align:center;line-height:0;white-space:nowrap;
  border:1px solid var(--line);border-radius:2px;color:var(--mute);
  transition:color .18s,border-color .18s,background .18s;
}
.iconbtn::before{content:"";display:inline-block;height:100%;vertical-align:middle;}
.iconbtn svg{
  display:inline-block;vertical-align:middle;width:18px;height:18px;
  pointer-events:none;              /* taps always land on the button itself */
}
#btnSearch svg{width:17px;height:17px;}
.iconbtn:hover{color:var(--ink);border-color:var(--violet-dim);}
.iconbtn.on{color:#fff;background:var(--violet);border-color:var(--violet);}

.menu-wrap{position:relative;flex:0 0 auto;}
.menu{
  position:absolute;top:calc(100% + 8px);right:0;min-width:210px;z-index:70;
  background:var(--card);border:1px solid var(--line);border-radius:2px;
  box-shadow:0 18px 44px rgba(0,0,0,.6);padding:6px;
}
.menu-who{padding:9px 11px 7px;color:var(--violet-lite);border-bottom:1px solid var(--line);margin-bottom:6px;}
.menu-item{
  display:block;width:100%;text-align:left;padding:10px 11px;border-radius:2px;
  font-size:14px;color:var(--ink);text-decoration:none;transition:background .15s;
}
.menu-item:hover{background:var(--deep);}
.menu-sep{height:1px;background:var(--line);margin:6px 0;}
.menu-row{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:8px 11px;}

.search{
  position:relative;display:flex;align-items:center;background:var(--deep);
  border:1px solid var(--line);border-radius:2px;height:32px;padding:0 10px;gap:8px;width:230px;
  transition:border-color .2s;
}
.search:focus-within{border-color:var(--violet-dim);}
#btnSearch{display:none;}   /* wide screens keep the field itself */
.search input{background:none;border:none;outline:none;color:var(--ink);width:100%;font-size:13px;}
.search input::placeholder{color:var(--mute);}
.search svg{flex:0 0 auto;opacity:.6;}

.navbtn{
  height:32px;padding:0 12px;border:1px solid var(--line);border-radius:2px;
  font-family:var(--mono);font-size:11px;letter-spacing:.1em;text-transform:uppercase;
  color:var(--mute);white-space:nowrap;transition:color .18s,border-color .18s,background .18s;
}
.navbtn:hover{color:var(--ink);border-color:var(--violet-dim);}
.navbtn.on{color:#fff;background:var(--violet);border-color:var(--violet);}
.navbtn.danger:hover{color:var(--danger);border-color:var(--danger);}

.lang{display:flex;border:1px solid var(--line);border-radius:2px;overflow:hidden;height:32px;}
.lang button{padding:0 9px;font-family:var(--mono);font-size:11px;color:var(--mute);}
.lang button.on{background:var(--violet);color:#fff;}

/* ---------- views ---------- */
.view{position:fixed;inset:var(--h) 0 0 0;overflow:auto;-webkit-overflow-scrolling:touch;}

.hero{padding:56px 32px 26px;max-width:1500px;margin:0 auto;}
.hero h1{
  font-family:var(--serif);font-weight:400;font-size:clamp(32px,5vw,60px);
  line-height:1.08;margin:0 0 18px;letter-spacing:-.015em;
}
.hero h1 i{color:var(--violet-lite);}
.hero p{max-width:52ch;color:var(--mute);font-size:15px;line-height:1.6;margin:0;}

.grid{display:grid;gap:22px;padding:14px 32px 80px;max-width:1500px;margin:0 auto;
      grid-template-columns:repeat(auto-fill,minmax(260px,1fr));}

.album{
  position:relative;text-align:left;display:block;width:100%;
  border:1px solid var(--line);background:var(--card);border-radius:2px;overflow:hidden;
  transition:border-color .22s,transform .22s;
}
/* Plain block layout: the card is a <button>, and flex inside one collapses
   in Safari. isolation keeps the clipping on its own layer so a rounded
   corner never leaks. */
.album .thumb{
  position:relative;display:block;aspect-ratio:4/3;
  overflow:hidden;isolation:isolate;background:var(--deep);
}
/* Filled by an absolutely positioned image rather than height:100%.
   A percentage height only resolves against a definite height, and a height
   derived from aspect-ratio is not one — Safari falls back to auto, the photo
   renders at its natural size, and the cover stops being 4:3 and pushes past
   the card. inset:0 resolves against the padding box no matter how the height
   was arrived at. */
.album .thumb img{
  position:absolute;inset:0;width:100%;height:100%;
  object-fit:cover;display:block;
}

/* Older engines without aspect-ratio keep the same box through padding. */
@supports not (aspect-ratio: 4 / 3){
  .album .thumb{height:0;padding-top:75%;}
}

/* Hover effects only where a pointer can actually hover. On a touch screen
   iOS keeps :hover applied after a tap, and a scaled child inside a rounded,
   clipped box is exactly the case Safari fails to clip — the cover spilled
   past the card. */
@media (hover:hover){
  .album .thumb img{transition:transform .5s ease;}
  .album:hover .thumb img{transform:scale(1.05);}
  .album:hover{border-color:var(--violet-dim);transform:translateY(-3px);}
}
.album .meta{padding:13px 14px 15px;}
.album .t{font-family:var(--serif);font-size:20px;line-height:1.22;}
.album .sub{margin-top:6px;}
/* A badge across the cover hid part of the photograph, so the album's title
   carries a small lock instead. */
.lock{
  display:inline-block;width:13px;height:13px;margin-right:6px;
  vertical-align:-1px;color:var(--violet-lite);opacity:.85;
}
.album .t span{overflow-wrap:anywhere;}
.empty{padding:60px 32px;color:var(--mute);font-family:var(--mono);font-size:12px;letter-spacing:.08em;}

/* ---------- sphere ---------- */
#sphereView{overflow:hidden;}
#cv{display:block;width:100%;height:100%;touch-action:none;cursor:grab;}
#cv.dragging{cursor:grabbing;}
.hud{
  position:absolute;left:0;right:0;bottom:0;padding:20px 24px;pointer-events:none;
  display:flex;align-items:flex-end;justify-content:space-between;gap:16px;
  background:linear-gradient(to top,rgba(10,13,19,.75),transparent);
}
.hud .hint{font-family:var(--mono);font-size:11px;letter-spacing:.08em;color:var(--mute);}
.caption{font-family:var(--mono);font-size:11px;letter-spacing:.1em;color:var(--violet-lite);min-height:14px;}
.loading{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);text-align:center;
         font-family:var(--mono);font-size:11px;letter-spacing:.16em;color:var(--mute);}
.bar{width:190px;height:1px;background:var(--line);margin-top:12px;}
.bar i{display:block;height:100%;width:0;background:var(--violet);transition:width .25s;}

/* ---------- lightbox ---------- */
#lightbox{position:fixed;inset:0;z-index:80;background:#05070B;display:flex;flex-direction:column;}
.lb-top{height:var(--h);flex:0 0 auto;display:flex;align-items:center;gap:16px;padding:0 18px;border-bottom:1px solid var(--line);}
.lb-top .name{cursor:default;}
.lb-stage{flex:1 1 auto;position:relative;min-height:0;}

/* Both frames fill a box of their own. Swapping an <img>'s src leaves it
   with no intrinsic size until the new file arrives, and a centred element
   with no size collapses and shows the broken-image placeholder off to one
   side — which is what paging quickly through a phone's connection looked
   like. Filling a fixed box means the layout never depends on load state. */
.lb-frame{position:absolute;inset:22px;}
.lb-frame img{
  position:absolute;inset:0;width:100%;height:100%;
  object-fit:contain;cursor:zoom-in;
}
/* the small variant is already cached from the grid, so it shows at once
   and the full-size one fades over it when it is genuinely ready */
#lbPreview{opacity:1;}
#lbImg{opacity:0;transition:opacity .2s;}
#lbImg.ready{opacity:1;}

@keyframes frameIn{
  from{opacity:0;transform:translate3d(var(--dx,0),0,0) scale(.96);}
  to{opacity:1;transform:none;}
}
.lb-frame.anim{animation:frameIn .3s cubic-bezier(.2,.75,.25,1);}

/* tap the frame to fold the chrome away — on a phone in landscape the top
   bar and the filmstrip together eat more height than the photo gets */
#lightbox.immersive .lb-top,
#lightbox.immersive .film{display:none;}
#lightbox.immersive .lb-frame{inset:0;}
#lightbox.immersive .arrow{opacity:.35;}
#lightbox.immersive .arrow:hover{opacity:1;}
#lightbox.immersive .lb-frame img{cursor:zoom-out;}
.arrow{
  position:absolute;top:50%;transform:translateY(-50%);width:46px;height:82px;
  padding:0;display:inline-block;text-align:center;line-height:0;
  background:rgba(17,22,33,.55);border:1px solid var(--line);color:var(--mute);
  transition:color .18s,border-color .18s,background .18s;
}
.arrow::before{content:"";display:inline-block;height:100%;vertical-align:middle;}
.arrow svg{display:inline-block;vertical-align:middle;pointer-events:none;}
.arrow:hover{color:#fff;background:var(--violet);border-color:var(--violet);}
.arrow.prev{left:14px;}
.arrow.next{right:14px;}
.film{
  flex:0 0 auto;height:104px;display:flex;gap:7px;align-items:center;padding:12px 14px;
  overflow-x:auto;overflow-y:hidden;border-top:1px solid var(--line);background:var(--deep);
}
.film::-webkit-scrollbar{height:6px;}
.film::-webkit-scrollbar-thumb{background:var(--line);}
.film img{
  height:78px;width:auto;flex:0 0 auto;object-fit:cover;display:block;opacity:.42;
  border:1px solid transparent;transition:opacity .18s,border-color .18s;cursor:pointer;
}
.film img:hover{opacity:.8;}
.film img.on{opacity:1;border-color:var(--violet-lite);}

/* ---------- modal ---------- */
.scrim{position:fixed;inset:0;z-index:90;background:rgba(5,7,11,.78);display:flex;align-items:center;justify-content:center;padding:20px;}
.modal{width:100%;max-width:420px;background:var(--card);border:1px solid var(--line);padding:28px;max-height:86vh;overflow:auto;}
.modal.wide{max-width:760px;}
.modal h2{font-family:var(--serif);font-weight:400;font-size:26px;margin:6px 0;}
.modal p{color:var(--mute);font-size:13px;line-height:1.55;margin:0 0 20px;}
.field{margin-bottom:12px;}
.field label{display:block;margin-bottom:6px;}
.field input,.field select,.field textarea{
  width:100%;height:38px;padding:0 11px;background:var(--deep);
  border:1px solid var(--line);border-radius:2px;color:var(--ink);outline:none;
}
.field textarea{height:auto;padding:9px 11px;resize:vertical;}
.field input:focus,.field select:focus,.field textarea:focus{border-color:var(--violet-dim);}
.check{display:flex;align-items:center;gap:9px;margin-bottom:12px;cursor:pointer;}
.check input{width:16px;height:16px;accent-color:var(--violet);}
.primary{
  width:100%;height:40px;background:var(--violet);color:#fff;border-radius:2px;margin-top:8px;
  font-family:var(--mono);font-size:11px;letter-spacing:.14em;text-transform:uppercase;
}
.primary:hover{background:#8A2BFF;}
.primary[disabled]{opacity:.5;cursor:default;}
.primary.danger{background:var(--danger);}
.row{display:flex;gap:10px;}
.row>*{flex:1 1 0;}
.note{margin-top:14px;font-family:var(--mono);font-size:10px;letter-spacing:.06em;color:var(--violet-lite);opacity:.8;line-height:1.6;}
.err{color:var(--danger);font-size:12px;margin-top:10px;min-height:16px;}

.toast{
  position:fixed;left:50%;bottom:26px;transform:translateX(-50%);z-index:100;
  background:var(--card);border:1px solid var(--violet-dim);padding:11px 18px;
  font-family:var(--mono);font-size:11px;letter-spacing:.08em;color:var(--ink);
  opacity:0;pointer-events:none;transition:opacity .3s,transform .3s;max-width:80vw;text-align:center;
}
.toast.show{opacity:1;transform:translateX(-50%) translateY(-6px);}

@media (max-width:820px){
  :root{--h:56px;}
  header{gap:8px;padding:0 10px;}
  #crumbName{font-size:16px;min-width:0;}
  /* the search field becomes a full-width overlay, opened by its icon */
  /* Closed until asked for. It used to rely on the hidden attribute, which
     nothing set on load, so the overlay sat over the albums from the start. */
  .search{display:none;}
  .search.open{
    display:flex;
    position:fixed;left:8px;right:8px;top:calc(var(--h) + 8px);width:auto;height:38px;z-index:65;
    box-shadow:0 14px 34px rgba(0,0,0,.55);
  }
  .menu{min-width:min(260px, calc(100vw - 20px));}
  .hide-sm{display:none !important;}
  #btnSearch{display:inline-block;}
  .hero{padding:30px 18px 16px;}
  .grid{padding:10px 18px 70px;gap:16px;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));}
  .album .t{font-size:18px;}
  .film{height:86px;}
  .film img{height:62px;}
  .arrow{width:38px;height:64px;}
  /* the original runs to tens of megabytes and a phone has nowhere useful
     to put it; the file stays reachable from a desktop browser */
  #lbDownload{display:none;}
}
/* landscape phones: almost no height to spare, so the filmstrip goes and
   the bars shrink before the user has to ask */
@media (max-height:520px) and (orientation:landscape){
  :root{--h:44px;}
  .film{display:none;}
  .lb-frame{inset:6px;}
  .lb-top{gap:10px;padding:0 10px;}
  .arrow{width:34px;height:56px;background:rgba(17,22,33,.35);}
}

@media (prefers-reduced-motion:reduce){
  *{transition-duration:.01ms !important;animation-duration:.01ms !important;}
}
