@import url('https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,400;0,9..144,500;0,9..144,600;1,9..144,400;1,9..144,500&family=Inter:wght@400;500;600&display=swap');

:root{
  --font-display: 'Fraunces', Georgia, serif;
  --font-body: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
  --space-1: 0.5rem;
  --space-2: 1rem;
  --space-3: 1.75rem;
  --space-4: 3rem;
  --space-5: 5rem;
  --space-6: 8rem;
}

*{ box-sizing: border-box; margin:0; padding:0; }
html{ scroll-behavior:smooth; }
body{
  font-family: var(--font-body);
  line-height:1.55;
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
}
img{ max-width:100%; display:block; }
a{ color:inherit; text-decoration:none; }
button{ font-family:inherit; cursor:pointer; }

/* Copyright: a small, quiet line at the end of the Photography and
   music pages, and in a corner of the landing page. */
.site-rights{
  text-align:center;
  font-size:0.72rem;
  letter-spacing:0.02em;
  color: var(--text-muted, #8a8378);
  opacity:0.75;
  padding: var(--space-4) var(--space-2) var(--space-3);
}
.landing-rights{
  position:fixed;
  left:50%; bottom:0.45rem;
  transform:translateX(-50%);
  z-index:5;
  font-size:0.62rem;
  letter-spacing:0.03em;
  color: rgba(237,232,221,0.45);
  white-space:nowrap;
  pointer-events:none;
}
/* Left/right page margins of the photo and music pages: the
   header, the location filter, every gallery and every album start and
   end at exactly the same distance from the window's edges. That
   distance is --page-margin, or more on a screen wide enough that the
   content would otherwise grow past --content-max-width (it then stays
   that wide, centered). Both values come from PAGE_LAYOUT and
   CONTENT_MAX_WIDTH in constants.js; the numbers after the commas are
   only fallbacks in case an old, cached constants.js is still around. */
.site-header,
.location-filter,
.gallery-section,
.album-section{
  padding-left:  max(var(--page-margin, 56px), calc((100% - var(--content-max-width, 1304px)) / 2));
  padding-right: max(var(--page-margin, 56px), calc((100% - var(--content-max-width, 1304px)) / 2));
}
/* The sections above now set the margins, so a .container inside them
   just fills the width they leave. */
.container{ width:100%; }
.visually-hidden{
  position:absolute; width:1px; height:1px; overflow:hidden;
  clip:rect(0 0 0 0); white-space:nowrap;
}

/* Shared page-arrival transition, used by the photography/music pages.
   Each one starts shifted off-screen (class set directly in the HTML, so
   there's no flash of it in the wrong place before this runs) and slides
   into place right after load — from the right for photography, from the
   left for music, picking up the direction the landing page's clicked
   panel slid off-screen in (see .panel-cover-clone in index.html and
   landing.js). Same duration as that clone's own transition (see
   TRANSITION_MS in constants.js, which sets the --transition-duration
   this reads), and the same one-viewport's-worth of distance too (100vw/
   100vh, deliberately NOT 100%: a percentage here would be relative to
   this wrapper's own full height, i.e. the whole page's scrollable
   length, which on a long gallery page is many times taller than the
   screen — sliding that whole distance in the same amount of time would
   make the arrival rush past far faster than the column that exits it).
   So both halves of the motion move the same distance in the same time —
   the same speed — rather than just nominally sharing a duration. */
.page-slide-wrap{
  transition: transform var(--transition-duration, 1100ms) cubic-bezier(.3,0,.2,1);
}
.page-slide-wrap.enter-from-right{ transform: translateX(100vw); }
.page-slide-wrap.enter-from-left{ transform: translateX(-100vw); }
/* On a narrow or short screen, index.html's own two panels stack one above
   the other instead of sitting side by side (see .split there), and
   landing.js's coverAndNavigate switches the exiting column to match —
   moving up/down instead of left/right: photography (on top) moves down,
   music (below) moves up. Each page then arrives continuing that same
   motion: photography ("from-right") slides down from the top, music
   ("from-left") slides up from the bottom. Keeping the same class names
   avoids needing any page-specific JS for this. */
@media (max-width: 720px), (max-height: 340px){
  .page-slide-wrap.enter-from-right{ transform: translateY(-100vh); }
  .page-slide-wrap.enter-from-left{ transform: translateY(100vh); }
}
@media (prefers-reduced-motion: reduce){
  *{ animation-duration:0.001ms !important; transition-duration:0.001ms !important; }
}

/* Shared dialog shell, used by the Biography and News dialogs on both
   sites. Colors come from each site's own CSS (--bg-elevated, --border,
   --text, --text-muted, --accent), so this single block looks right
   whichever site includes it. */
.site-dialog{
  margin:auto;            /* centred on the screen (the "* { margin:0 }" reset above would pin it to the top-left corner) */
  border:1px solid var(--border);
  border-radius:10px;
  background: var(--bg-elevated);
  color: var(--text);
  /* Side padding lives on the header and the scrolling body instead of
     here, so the body's scrollbar (when there is one) sits at the dialog's
     edge and the text keeps the same margin on its left and right. */
  padding: var(--space-3) 0;
  width: min(32rem, 90vw);
  /* As tall as the window allows (minus a small margin), so the body only
     scrolls when its content really doesn't fit on screen. */
  max-height: calc(100vh - 2rem);
  max-height: calc(100dvh - 2rem);
}
/* Only while open: a closed <dialog> must keep its default display:none. */
.site-dialog[open]{
  display:flex;
  flex-direction:column;
}
.site-dialog::backdrop{ background: rgba(20,20,18,0.55); }
.site-dialog-header{
  display:flex;
  justify-content:space-between;
  align-items:center;
  margin-bottom: var(--space-2);
  padding: 0 var(--space-3);
  font-family: var(--font-display);
  flex:none;
}
.site-dialog-header h3{ font-weight:500; font-size:1.3rem; }
.site-dialog-close{
  background:none; border:none; color: var(--text);
  font-size:1.4rem; line-height:1;
  padding:0;
}
.site-dialog-body{
  overflow-y:auto;
  /* Fills whatever height the dialog has left under the header, and
     scrolls only past that. */
  flex:1 1 auto;
  min-height:0;
  padding: 0 var(--space-3);
}

/* Biography dialog */
.bio-photo{
  position:relative;
  width:100%;
  aspect-ratio: 4/3;
  border-radius:8px;
  overflow:hidden;
  background: var(--bg);
  border:1px solid var(--border);
  margin-bottom: var(--space-2);
}
.bio-photo img{ width:100%; height:100%; object-fit:cover; }
.bio-photo.placeholder::after{
  content:'Portrait';
  position:absolute; inset:0;
  display:flex; align-items:center; justify-content:center;
  font-family: var(--font-display);
  font-style:italic;
  color: var(--text-muted);
}
/* Same typeface as "Antonio Sbrolli" everywhere else it appears — the
   landing page's big name and the site-title link in both sub-sites'
   headers (see .center-mark .name in index.html and .site-title in
   photography.css/music.css). */
.bio-name{
  font-family: var(--font-body);
  font-size:1.4rem;
  font-weight:500;
  margin-bottom:0.15rem;
}
.bio-tagline{
  color: var(--text-muted);
  font-size:0.9rem;
  margin-bottom: var(--space-2);
}
.bio-lang{ display:flex; gap:0.4rem; margin-bottom: var(--space-2); }
.bio-lang button{
  font-family: var(--font-body);
  font-size:0.75rem;
  font-weight:500;
  letter-spacing:0.02em;
  color: var(--text-muted);
  background:none;
  border:1px solid var(--border);
  border-radius:999px;
  padding:0.2rem 0.65rem;
  cursor:pointer;
  transition: color 0.2s ease, border-color 0.2s ease, background 0.2s ease;
}
.bio-lang button:hover{ color: var(--text); border-color: var(--text-muted); }
.bio-lang button.active{
  color: var(--bg);
  background: var(--text);
  border-color: var(--text);
}
.bio-body p{ margin-bottom: var(--space-2); font-size:0.95rem; }
.bio-body p:last-child{ margin-bottom:0; }
.bio-body .bio-signature{
  margin-top: var(--space-3);
  margin-bottom:0.2rem;
  font-family: var(--font-display);
  font-style:italic;
  color: var(--text-muted);
  text-align:right;
}
.bio-body .bio-email{ text-align:right; font-size:0.9rem; }
.bio-body .bio-email a{
  color: var(--accent);
  border-bottom:1px solid transparent;
}
.bio-body .bio-email a:hover{ border-color: var(--accent); }
/* Profile links under the text: Instagram left, music services right. */
.bio-links{
  display:flex;
  justify-content:space-between;
  align-items:center;
  margin-top: var(--space-3);
  padding-top: var(--space-2);
  border-top:1px solid var(--border);
}
.bio-links[hidden]{ display:none; }
.bio-links-left, .bio-links-right{ display:flex; gap:0.7rem; }
.bio-link{ display:block; width:32px; height:32px; line-height:0; transition: transform 0.15s ease, opacity 0.15s ease; }
.bio-link svg{ width:100%; height:100%; display:block; }
.bio-link:hover{ transform: translateY(-2px); }
.bio-link:focus-visible{ outline:2px solid var(--accent); outline-offset:3px; border-radius:6px; }

/* The halftone behind the photo and music pages (page-halftone.js):
   the same tiles as the home page, fixed, under a veil of the page's colour. */
.page-halftone{
  position:fixed; inset:0; z-index:-1;
  display:grid;
  grid-template-columns: repeat(var(--cols, 6), 1fr);
  grid-template-rows: repeat(var(--rows, 4), 1fr);
  pointer-events:none;
}
/* PAGE_HALFTONE_MOTION 'scroll': part of the page (it scrolls with it, in step) —
   a wrapper as tall as the page clips the layer, which page-halftone.js moves by whole rounds */
.page-halftone-wrap{ position:absolute; left:0; right:0; top:0; bottom:0; z-index:-1; overflow:hidden; pointer-events:none; }
.page-halftone.native{ position:absolute; top:0; left:0; right:0; bottom:auto; z-index:auto; }
/* 'parallax': fixed, taller than the screen, shifted by a scroll-driven animation (or a script) */
.page-halftone.moving{ bottom:auto; will-change:transform; }
@keyframes pht-parallax{ from{ transform: translate3d(0, 0, 0); } to{ transform: translate3d(0, var(--pht-shift, 0px), 0); } }
.page-halftone-tile{ position:relative; overflow:hidden; background:#141311; }
.page-halftone-tile img{
  display:block; width:100%; height:100%; object-fit:cover;
  filter: var(--ht-filter, grayscale(1) contrast(1.15) brightness(0.48));   /* HALFTONE_* in constants.js */
}
.page-halftone-tile::after{
  content:''; position:absolute; inset:0;
  background-image: var(--ht-screen, radial-gradient(circle, transparent 0 1.7px, rgba(20,19,17,0.7) 2.4px));
  background-size: var(--ht-spacing, 5px) var(--ht-spacing, 5px);
}
.page-halftone::after{
  content:''; position:absolute; inset:0;
  background: var(--bg);
  opacity: var(--pht-veil, 0.75);     /* 1 - PAGE_HALFTONE_STRENGTH */
}

/* "?" dialog: how to use the site (data/help.js, initHelpDialog in news.js) */
.help-dialog{ width: min(36rem, 92vw); }
.help-intro{ color: var(--text-muted); font-size:0.92rem; margin-bottom: var(--space-2); }
.help-h{
  font-family: var(--font-display);
  font-weight:500;
  font-size:1.05rem;
  margin: var(--space-2) 0 0.35rem;
}
.help-list{ margin:0 0 0 1.1rem; padding:0; font-size:0.9rem; line-height:1.5; }
.help-list li{ margin-bottom:0.35rem; }
.help-list b{ font-weight:600; }
.help-note{
  margin-top: var(--space-3);
  padding-top: var(--space-2);
  border-top:1px solid var(--border);
  font-size:0.8rem;
  color: var(--text-muted);
}

/* News dialog */
.news-list{ display:flex; flex-direction:column; gap: var(--space-2); }
.news-item{
  display:block;
  border:1px solid var(--border);
  border-radius:8px;
  padding: var(--space-2);
  color: var(--text);
}
.news-item:hover{ border-color: var(--accent); }
.news-item-top{
  display:flex;
  justify-content:space-between;
  align-items:center;
  gap: var(--space-2);
  margin-bottom:0.35rem;
  font-size:0.75rem;
}
.news-badge{
  padding:0.15rem 0.6rem;
  border-radius:999px;
  border:1px solid var(--accent);
  color: var(--accent);
  white-space:nowrap;
}
.news-item-date{ color: var(--text-muted); white-space:nowrap; }
.news-item-title{ font-weight:500; margin-bottom:0.2rem; }
.news-item-desc{ font-size:0.85rem; color: var(--text-muted); }
.news-empty{ color: var(--text-muted); font-size:0.9rem; }
/* The newsletter box under the news (initNewsletter in news.js) */
.news-subscribe{
  margin-top: var(--space-3);
  padding-top: var(--space-2);
  border-top:1px solid var(--border);
}
.news-subscribe-title{ font-family: var(--font-display); font-size:1.05rem; font-weight:500; margin-bottom:0.2rem; }
.news-subscribe-text{ font-size:0.85rem; color: var(--text-muted); margin-bottom:0.6rem; }
.news-subscribe-row{ display:flex; gap:0.5rem; }
.news-subscribe-row input{
  flex:1 1 auto; min-width:0;
  font: inherit; font-size:0.9rem;
  color: var(--text); background: transparent;
  border:1px solid var(--border); border-radius:6px;
  padding:0.45rem 0.7rem;
}
.news-subscribe-row input:focus{ outline:none; border-color: var(--accent); }
.news-subscribe-row button{
  flex:none;
  font-size:0.85rem; font-weight:500;
  color: var(--bg-elevated); background: var(--accent);
  border:1px solid var(--accent); border-radius:6px;
  padding:0.45rem 0.9rem;
}
.news-subscribe-row button:disabled{ opacity:0.6; cursor:default; }
.news-subscribe-msg{ font-size:0.82rem; margin-top:0.45rem; min-height:1em; color: var(--accent); }
.news-subscribe-msg:empty{ display:none; }
.news-subscribe-msg.bad{ color:#d98a7a; }
.news-subscribe-privacy{ font-size:0.72rem; color: var(--text-muted); margin-top:0.5rem; }
.news-subscribe-privacy a{ text-decoration:underline; }

/* Smartwatch (the "watch" row of PAGE_LAYOUT in constants.js, which sets
   <html data-layout>): the site header wraps onto two lines with smaller
   text instead of running off the side of the tiny screen. */
html[data-layout="watch"] .site-header{ flex-wrap:wrap; gap: 0.3rem var(--space-2); }
html[data-layout="watch"] .site-title{ font-size:0.95rem; }
html[data-layout="watch"] .site-nav{ gap: 0.3rem 0.8rem; flex-wrap:wrap; }
html[data-layout="watch"] .site-nav a, html[data-layout="watch"] .site-nav button{ font-size:0.75rem; }

/* photo and music pages: the header row (name, ↑ ↓ and the
   link to the other site) stays pinned to the top of the window while the
   page scrolls under it. Its own background hides what passes beneath.
   --sticky-header-h (set by page-nav.js) keeps anything the page scrolls
   to from ending up hidden behind it. */
html{ scroll-padding-top: var(--sticky-header-h, 0px); }
.site-header{
  position: sticky;
  top: 0;
  z-index: 40;
  background: var(--bg);
}
.site-nav .scroll-btn{
  font-size:1.15rem;
  line-height:1;
  padding: 0.15rem 0.35rem;
  border-bottom:none;
  transition: opacity 0.2s ease, color 0.2s ease;
}
.site-nav .scroll-btn.at-end{ opacity:0.3; }
.site-nav .scroll-btn{ display:inline-flex; align-items:center; justify-content:center; }
/* The empty markers ↑ and ↓ link to (see page-nav.js). */
.page-anchor{ height:0; }
#page-top{ scroll-margin-top: 0; }
/* Bigger tap target on touchscreens, without changing how it looks. */
.site-nav .scroll-btn{ touch-action: manipulation; min-width:2rem; min-height:2rem; }
html[data-layout="watch"] .site-nav .scroll-btn{ font-size:0.95rem; }

/* The links to the other pages (.nav-pages: Images, Images & Sounds, Sounds): side by side; on a phone one above
   the other, right-aligned, smaller — so the header stays one line high. */
.site-nav .nav-pages{ display:flex; align-items:center; gap: inherit; }
@media (max-width: 600px){
  .site-nav .nav-pages{ flex-direction:column; align-items:flex-end; gap:0.2rem; }
  .site-nav .nav-pages a{ white-space:nowrap; font-size:0.85rem; line-height:1.25; }
  .site-header .site-nav{ gap: var(--space-2); }
}
html[data-layout="watch"] .site-nav .nav-pages{ flex-direction:row; flex-wrap:wrap; gap:0.3rem 0.8rem; }