/*
Theme Name: House Ozaki
Theme URI: https://houseozaki.com
Description: Child theme of Astra for House Ozaki. Holds the approved House Ozaki design system — design tokens, typography, and component styles ported verbatim from the signed-off demo. All custom site styling lives here so Astra updates never overwrite it.
Author: House Ozaki
Template: astra
Version: 1.1.2
Requires at least: 6.0
Requires PHP: 7.4
Text Domain: houseozaki
*/

/* ============================================================
   House Ozaki design system — ported as-is from the approved demo.
   Tokens, base, components, and responsive rules below are the
   single source of visual truth and load site-wide.
   ============================================================ */

:root{
    --bg:#0a0a0a;
    --bg-raised:#131311;
    --bg-raised-hover:#191917;
    --text:#f2f2ed;
    --text-muted:#93938c;
    --text-faint:#5a5a55;
    --border:rgba(242,242,237,0.12);
    --border-strong:rgba(242,242,237,0.24);
    --font-display:'Space Grotesk', sans-serif;
    --font-body:'Inter', sans-serif;
    --font-mono:'IBM Plex Mono', monospace;
    --maxw:1120px;
    --space-1:0.5rem;
    --space-2:1rem;
    --space-3:1.5rem;
    --space-4:2.25rem;
    --space-5:3.5rem;
    --space-6:5.5rem;
    --space-7:8rem;
    --ease:cubic-bezier(0.16,1,0.3,1);
  }

  *{box-sizing:border-box;margin:0;padding:0;}

  html{scroll-behavior:smooth;}

  body{
    background:var(--bg);
    color:var(--text);
    font-family:var(--font-body);
    font-size:16px;
    line-height:1.6;
    -webkit-font-smoothing:antialiased;
    overflow-x:hidden;
  }

  img{max-width:100%;display:block;}

  a{color:inherit;text-decoration:none;}

  ::selection{background:var(--text);color:var(--bg);}

  :focus-visible{
    outline:1.5px solid var(--text);
    outline-offset:3px;
  }

  .wrap{
    max-width:var(--maxw);
    margin:0 auto;
    padding-left:clamp(1.25rem, 4vw, 3rem);
    padding-right:clamp(1.25rem, 4vw, 3rem);
  }

  .eyebrow{
    font-family:var(--font-mono);
    font-size:0.75rem;
    letter-spacing:0.16em;
    text-transform:uppercase;
    color:var(--text-muted);
    display:flex;
    align-items:center;
    gap:0.6rem;
  }

  .eyebrow::before{
    content:'';
    width:14px;
    height:1px;
    background:var(--border-strong);
    display:inline-block;
  }

  /* ---------- mark ---------- */

  .mark{
    display:inline-flex;
    align-items:center;
    gap:0.6rem;
  }

  .mark img{
    height:28px;
    width:auto;
    opacity:0.96;
  }

  .mark span{
    font-family:var(--font-display);
    font-weight:400;
    font-size:0.95rem;
    letter-spacing:0.1em;
    color:var(--text);
  }

  /* ---------- nav ---------- */

  .nav{
    position:fixed;
    top:0;left:0;right:0;
    z-index:100;
    border-bottom:1px solid transparent;
    transition:border-color 0.4s var(--ease), background-color 0.4s var(--ease);
  }

  .nav.is-scrolled{
    border-bottom-color:var(--border);
    background:rgba(10,10,10,0.82);
    backdrop-filter:blur(10px);
  }

  .nav__inner{
    display:flex;
    align-items:center;
    justify-content:space-between;
    height:72px;
  }

  .nav__links{
    display:flex;
    gap:clamp(1.25rem, 2.5vw, 2.25rem);
    list-style:none;
  }

  .nav__links a{
    font-size:0.85rem;
    color:var(--text-muted);
    transition:color 0.25s var(--ease);
    position:relative;
  }

  .nav__links a::after{
    content:'';
    position:absolute;
    left:0;right:0;bottom:-4px;
    height:1px;
    background:var(--text);
    transform:scaleX(0);
    transform-origin:left;
    transition:transform 0.3s var(--ease);
  }

  .nav__links a:hover{color:var(--text);}
  .nav__links a:hover::after{transform:scaleX(1);}

  .nav__toggle{
    display:none;
    width:32px;height:32px;
    border:1px solid var(--border-strong);
    border-radius:4px;
    background:none;
    color:var(--text);
    align-items:center;
    justify-content:center;
    cursor:pointer;
  }

  /* ---------- buttons ---------- */

  .btn{
    display:inline-flex;
    align-items:center;
    gap:0.5rem;
    font-family:var(--font-body);
    font-size:0.85rem;
    font-weight:500;
    padding:0.85rem 1.5rem;
    border-radius:3px;
    border:1px solid var(--text);
    color:var(--text);
    background:transparent;
    cursor:pointer;
    transition:background-color 0.3s var(--ease), color 0.3s var(--ease);
    white-space:nowrap;
  }

  .btn:hover{background:var(--text);color:var(--bg);}

  .btn--solid{background:var(--text);color:var(--bg);}
  .btn--solid:hover{background:var(--text-muted);border-color:var(--text-muted);}

  .btn--ghost{border-color:var(--border-strong);color:var(--text-muted);}
  .btn--ghost:hover{border-color:var(--text);color:var(--text);background:transparent;}

  .btn--disabled{
    border-color:var(--border);
    color:var(--text-faint);
    cursor:default;
    pointer-events:none;
  }

  .btn--disabled:hover{background:transparent;color:var(--text-faint);}

  /* ---------- skyline motif (signature element) ---------- */

  .skyline{
    position:absolute;
    inset:0;
    display:flex;
    align-items:flex-end;
    justify-content:center;
    gap:clamp(6px, 1vw, 14px);
    opacity:0.85;
    pointer-events:none;
    overflow:hidden;
  }

  .skyline span{
    width:clamp(6px, 1vw, 16px);
    background:linear-gradient(to top, rgba(242,242,237,0.10), rgba(242,242,237,0.02));
    transform:scaleY(0);
    transform-origin:bottom;
    transition:transform 1.1s var(--ease);
  }

  .skyline.is-visible span{transform:scaleY(1);}

  .divider-bars{
    display:flex;
    align-items:flex-end;
    gap:5px;
    height:22px;
    margin-bottom:var(--space-3);
  }

  .divider-bars span{
    width:4px;
    background:var(--border-strong);
  }

  /* ---------- hero ---------- */

  .hero{
    position:relative;
    min-height:100vh;
    display:flex;
    align-items:center;
    padding-top:72px;
  }

  .hero__inner{
    position:relative;
    z-index:2;
    max-width:760px;
  }

  .hero h1{
    font-family:var(--font-display);
    font-weight:600;
    font-size:clamp(2.6rem, 6.4vw, 5rem);
    line-height:1.04;
    letter-spacing:-0.01em;
    margin:var(--space-3) 0 var(--space-3);
  }

  .hero p{
    font-size:clamp(1rem, 1.6vw, 1.15rem);
    color:var(--text-muted);
    max-width:480px;
    margin-bottom:var(--space-4);
  }

  .hero__actions{
    display:flex;
    gap:1rem;
    flex-wrap:wrap;
  }

  .reveal{
    opacity:0;
    transform:translateY(18px);
    transition:opacity 0.8s var(--ease), transform 0.8s var(--ease);
  }

  .reveal.is-visible{
    opacity:1;
    transform:translateY(0);
  }

  /* ---------- sections ---------- */

  section{
    padding:var(--space-7) 0;
    border-top:1px solid var(--border);
  }

  .section__head{
    max-width:620px;
    margin-bottom:var(--space-5);
  }

  .section__head h2{
    font-family:var(--font-display);
    font-weight:600;
    font-size:clamp(1.9rem, 3.6vw, 2.75rem);
    margin:var(--space-2) 0 var(--space-2);
    letter-spacing:-0.01em;
  }

  .section__head p{
    color:var(--text-muted);
    max-width:480px;
  }

  /* ---------- projects ---------- */

  .projects__grid{
    display:grid;
    grid-template-columns:repeat(3, 1fr);
    gap:1px;
    background:var(--border);
    border:1px solid var(--border);
  }

  .card{
    background:var(--bg);
    padding:var(--space-4);
    display:flex;
    flex-direction:column;
    gap:var(--space-2);
    transition:background-color 0.3s var(--ease);
  }

  .card:hover{background:var(--bg-raised);}

  .card{cursor:pointer;}

  button.card{
    appearance:none;
    -webkit-appearance:none;
    border:none;
    font:inherit;
    text-align:left;
    width:100%;
  }

  .card__link{
    margin-top:auto;
    padding-top:0.5rem;
    font-family:var(--font-mono);
    font-size:0.78rem;
    color:var(--text-faint);
    transition:color 0.25s var(--ease);
  }

  .card:hover .card__link{color:var(--text);}

  /* ---------- screens ---------- */

  .screen{display:none;}
  .screen.is-active{display:block;}

  .text-link{
    display:inline-flex;
    align-items:center;
    gap:0.4rem;
    font-family:var(--font-mono);
    font-size:0.82rem;
    color:var(--text-muted);
    border-bottom:1px solid var(--border-strong);
    padding-bottom:2px;
    width:fit-content;
    transition:color 0.25s var(--ease), border-color 0.25s var(--ease);
  }

  .text-link:hover{color:var(--text);border-color:var(--text);}

  /* ---------- projects browser ---------- */

  .projects-page{padding-top:150px;}

  .projects-page__head{margin:var(--space-3) 0 var(--space-3);}

  .projects-page__head h2{
    font-family:var(--font-display);
    font-weight:600;
    font-size:clamp(1.9rem, 3.4vw, 2.6rem);
    margin:0.6rem 0 var(--space-2);
    letter-spacing:-0.01em;
  }

  .projects-page__head p{color:var(--text-muted);max-width:480px;}

  .view-switch{
    display:flex;
    gap:0.5rem;
    margin-bottom:var(--space-4);
  }

  .view-switch__btn{
    font-family:var(--font-mono);
    font-size:0.72rem;
    letter-spacing:0.08em;
    text-transform:uppercase;
    padding:0.55rem 1rem;
    border:1px solid var(--border);
    background:none;
    color:var(--text-muted);
    cursor:pointer;
    border-radius:2px;
    transition:background-color 0.2s var(--ease), color 0.2s var(--ease), border-color 0.2s var(--ease);
  }

  .view-switch__btn:hover{color:var(--text);border-color:var(--border-strong);}

  .view-switch__btn.is-active{background:var(--text);color:var(--bg);border-color:var(--text);}

  .browser{
    display:flex;
    border:1px solid var(--border);
    height:calc(100vh - 280px);
    min-height:420px;
    margin-bottom:var(--space-3);
  }

  .browser__list{
    width:360px;
    flex-shrink:0;
    overflow-y:auto;
    border-right:1px solid var(--border);
  }

  .browser__detail{
    flex-grow:1;
    overflow-y:auto;
    padding:var(--space-4) var(--space-5);
  }

  .browser__list.mode-mosaic{
    display:grid;
    grid-template-columns:repeat(2, 1fr);
    gap:1px;
    background:var(--border);
    padding:1px;
    align-content:start;
  }

  .list-item{
    display:flex;
    gap:0.85rem;
    align-items:flex-start;
    padding:0.9rem 1.1rem;
    border-bottom:1px solid var(--border);
    cursor:pointer;
    transition:background-color 0.2s var(--ease);
  }

  .list-item:hover{background:var(--bg-raised);}
  .list-item.is-selected{background:var(--bg-raised);box-shadow:inset 2px 0 0 var(--text);}

  .list-item__thumb{
    width:46px;height:46px;
    flex-shrink:0;
    background:var(--bg-raised);
    border:1px solid var(--border);
    color:var(--text-faint);
    display:flex;
    align-items:center;
    justify-content:center;
    padding:9px;
  }

  .list-item__thumb svg{width:100%;height:100%;}

  .list-item__title{
    font-family:var(--font-display);
    font-weight:500;
    font-size:0.92rem;
  }

  .list-item__meta{
    font-family:var(--font-mono);
    font-size:0.68rem;
    letter-spacing:0.04em;
    color:var(--text-faint);
  }

  .list-item__summary{
    font-size:0.78rem;
    color:var(--text-muted);
  }

  .mosaic-item{
    position:relative;
    aspect-ratio:1;
    background:var(--bg);
    cursor:pointer;
    overflow:hidden;
    display:flex;
    align-items:center;
    justify-content:center;
    transition:background-color 0.2s var(--ease);
  }

  .mosaic-item:hover{background:var(--bg-raised);}
  .mosaic-item.is-selected{outline:2px solid var(--text);outline-offset:-2px;}

  .mosaic-item__icon{
    width:36%;
    color:var(--text-faint);
    opacity:0.5;
  }

  .mosaic-item__icon svg{width:100%;display:block;}

  .mosaic-item__label{
    position:absolute;
    left:0;right:0;bottom:0;
    padding:0.6rem 0.7rem;
    background:linear-gradient(to top, rgba(10,10,10,0.85), transparent);
    font-family:var(--font-mono);
    font-size:0.66rem;
  }

  .compact-item{
    display:flex;
    justify-content:space-between;
    align-items:baseline;
    gap:0.75rem;
    padding:0.7rem 1.1rem;
    border-bottom:1px solid var(--border);
    cursor:pointer;
    font-size:0.85rem;
    transition:background-color 0.2s var(--ease);
  }

  .compact-item:hover{background:var(--bg-raised);}
  .compact-item.is-selected{background:var(--bg-raised);box-shadow:inset 2px 0 0 var(--text);}

  .compact-item__title{font-family:var(--font-body);font-weight:500;}

  .compact-item__meta{
    font-family:var(--font-mono);
    font-size:0.68rem;
    color:var(--text-faint);
    white-space:nowrap;
  }

  .detail-title{
    font-family:var(--font-display);
    font-weight:600;
    font-size:clamp(1.5rem, 2.4vw, 2rem);
    margin:0.5rem 0 0.75rem;
  }

  .detail-body p{
    color:var(--text-muted);
    margin-bottom:1rem;
    max-width:560px;
  }

  /* ---------- membership tiers ---------- */

  .tier-grid{
    display:grid;
    grid-template-columns:repeat(3, 1fr);
    gap:1px;
    background:var(--border);
    border:1px solid var(--border);
    margin-bottom:var(--space-6);
  }

  .tier-card{
    background:var(--bg);
    padding:var(--space-4);
    display:flex;
    flex-direction:column;
    gap:0.65rem;
  }

  .tier-card--locked{opacity:0.62;}

  .tier-card__price{
    font-family:var(--font-mono);
    font-size:0.72rem;
    letter-spacing:0.1em;
    text-transform:uppercase;
    color:var(--text-faint);
  }

  .tier-card__name{
    font-family:var(--font-display);
    font-weight:600;
    font-size:1.3rem;
  }

  .tier-card__intro{
    color:var(--text-muted);
    font-size:0.88rem;
  }

  .tier-card__benefits{
    list-style:none;
    display:flex;
    flex-direction:column;
    gap:0.55rem;
    margin:0.25rem 0 0.5rem;
    flex-grow:1;
  }

  .tier-card__benefits li{
    font-size:0.85rem;
    color:var(--text-muted);
    padding-left:1.1rem;
    position:relative;
  }

  .tier-card__benefits li::before{
    content:'—';
    position:absolute;
    left:0;
    color:var(--text-faint);
  }

  .card__meta{
    font-family:var(--font-mono);
    font-size:0.72rem;
    letter-spacing:0.1em;
    text-transform:uppercase;
    color:var(--text-faint);
  }

  .card h3{
    font-family:var(--font-display);
    font-weight:600;
    font-size:1.3rem;
    margin-top:0.25rem;
  }

  .card__date{
    font-family:var(--font-mono);
    font-size:0.78rem;
    color:var(--text-muted);
  }

  .card p{
    color:var(--text-muted);
    font-size:0.9rem;
    flex-grow:1;
  }

  .badge{
    display:inline-flex;
    align-items:center;
    gap:0.4rem;
    font-family:var(--font-mono);
    font-size:0.72rem;
    letter-spacing:0.08em;
    text-transform:uppercase;
    color:var(--text-faint);
    border:1px solid var(--border);
    border-radius:2px;
    padding:0.5rem 0.75rem;
    width:fit-content;
  }

  /* ---------- about ---------- */

  .about__grid{
    display:grid;
    grid-template-columns:1.3fr 1fr;
    gap:var(--space-6);
    align-items:start;
  }

  .about__lede{
    font-family:var(--font-display);
    font-weight:500;
    font-size:clamp(1.4rem, 2.4vw, 1.9rem);
    line-height:1.4;
    margin-bottom:var(--space-3);
  }

  .about__body{
    color:var(--text-muted);
    max-width:520px;
  }

  .spec{
    border:1px solid var(--border);
    padding:var(--space-3);
  }

  .spec__row{
    display:flex;
    justify-content:space-between;
    gap:1rem;
    padding:0.7rem 0;
    border-bottom:1px solid var(--border);
    font-family:var(--font-mono);
    font-size:0.8rem;
  }

  .spec__row:last-child{border-bottom:none;}

  .spec__row span:first-child{
    color:var(--text-faint);
    letter-spacing:0.08em;
  }

  /* ---------- forms ---------- */

  .form-section__grid{
    display:grid;
    grid-template-columns:1fr 1fr;
    gap:var(--space-6);
    align-items:start;
  }

  .form{
    display:flex;
    flex-direction:column;
    gap:1rem;
    max-width:420px;
  }

  .form__row{
    display:flex;
    flex-direction:column;
    gap:0.4rem;
  }

  .form label{
    font-family:var(--font-mono);
    font-size:0.7rem;
    letter-spacing:0.1em;
    text-transform:uppercase;
    color:var(--text-faint);
  }

  .form input,
  .form textarea{
    background:var(--bg-raised);
    border:1px solid var(--border);
    color:var(--text);
    font-family:var(--font-body);
    font-size:0.95rem;
    padding:0.85rem 1rem;
    border-radius:2px;
    transition:border-color 0.25s var(--ease);
  }

  .form input:focus,
  .form textarea:focus{
    border-color:var(--border-strong);
    outline:none;
  }

  .form textarea{resize:vertical;min-height:110px;}

  .form__note{
    font-size:0.78rem;
    color:var(--text-faint);
  }

  .contact__direct{
    font-family:var(--font-display);
    font-weight:500;
    font-size:clamp(1.3rem, 2.2vw, 1.7rem);
  }

  .contact__direct a{
    border-bottom:1px solid var(--border-strong);
    transition:border-color 0.25s var(--ease), color 0.25s var(--ease);
  }

  .contact__direct a:hover{border-color:var(--text);}

  /* ---------- footer ---------- */

  footer{
    border-top:1px solid var(--border);
    padding:var(--space-2) 0 var(--space-2);
  }

  .footer__top{
    display:flex;
    justify-content:space-between;
    align-items:flex-start;
    flex-wrap:wrap;
    gap:var(--space-3);
    margin-bottom:var(--space-2);
  }

  .footer__links{
    display:flex;
    gap:1.75rem;
    list-style:none;
    flex-wrap:wrap;
  }

  .footer__links a{
    font-size:0.85rem;
    color:var(--text-muted);
    transition:color 0.25s var(--ease);
  }

  .footer__links a:hover{color:var(--text);}

  .footer__bottom{
    display:flex;
    justify-content:space-between;
    flex-wrap:wrap;
    gap:0.75rem;
    font-family:var(--font-mono);
    font-size:0.72rem;
    color:var(--text-faint);
    padding-top:var(--space-2);
    border-top:1px solid var(--border);
  }

  /* ---------- responsive ---------- */

  @media (max-width:860px){
    .projects__grid{grid-template-columns:1fr;}
    .about__grid{grid-template-columns:1fr;}
    .form-section__grid{grid-template-columns:1fr;}
    .browser{flex-direction:column;height:auto;}
    .browser__list{width:100%;max-height:280px;border-right:none;border-bottom:1px solid var(--border);}
    .browser__list.mode-mosaic{grid-template-columns:repeat(3, 1fr);}
    .browser__detail{padding:var(--space-3);}
    .tier-grid{grid-template-columns:1fr;}
  }

  @media (max-width:680px){
    .nav__links{
      position:fixed;
      top:72px;left:0;right:0;
      flex-direction:column;
      background:var(--bg);
      border-bottom:1px solid var(--border);
      padding:var(--space-3) clamp(1.25rem, 4vw, 3rem);
      gap:1.1rem;
      transform:translateY(-130%);
      transition:transform 0.35s var(--ease);
    }
    .nav__links.is-open{transform:translateY(0);}
    .nav__toggle{display:flex;}
    section{padding:var(--space-6) 0;}
    .projects-page{padding-top:120px;}
  }

  @media (prefers-reduced-motion:reduce){
    *{transition-duration:0.01ms !important; animation-duration:0.01ms !important;}
    .reveal{opacity:1;transform:none;}
    .skyline span{transform:scaleY(1);}
  }

  /* ============================================================
     House Ozaki — browser rework & fixes (flexible model)
     ============================================================ */

  /* #2 Stable pane width: fill the wrap, stop content-driven reflow */
  .browser{width:100%;}
  .browser__detail{min-width:0;}
  .detail-title{overflow-wrap:break-word;}

  /* #3 Quieter, on-brand action button: outlined, fills on hover */
  .btn--ghost{color:var(--text);}
  .btn--ghost:hover{background:var(--text);color:var(--bg);border-color:var(--text);}
  /* clean (non-emoji) outbound arrow for external buttons */
  .btn--external::after{content:"\2197\FE0E";font-size:0.9em;}

  /* detail panel actions row */
  .detail-actions{margin-top:1.25rem;display:flex;flex-wrap:wrap;align-items:center;gap:1rem;}
  .detail-fulllink{font-size:0.78rem;}

  /* #5 Featured image support in the browser */
  .list-item__thumb--img{padding:0;}
  .list-item__thumb--img img{width:100%;height:100%;object-fit:cover;display:block;}
  .mosaic-item__img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;}
  .detail-media{margin:1.25rem 0;border:1px solid var(--border);max-width:560px;}
  .detail-media img{display:block;width:100%;height:auto;}

  /* #6 Compact: one clean line per item, truncate long titles */
  .compact-item__title{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
  .compact-item__meta{flex-shrink:0;}

  /* ============================================================
     #2 (v2) Width stability — stop Astra's content column from
     shrink-wrapping to the browser's content on the projects page.
     Scoped to the projects archive body class so header/footer
     layout is untouched.
     ============================================================ */
  .post-type-archive-project #content .ast-container{display:block;}
  .post-type-archive-project #primary{flex:1 1 100%;width:100%;max-width:100%;}
  .post-type-archive-project #main,
  .post-type-archive-project .site-main{width:100%;}
  .projects-page{width:100%;}
  .browser{width:100%;}
  .browser__detail{min-width:0;flex-basis:0;}

  /* ============================================================
     Replace Astra's stock header/footer with House Ozaki's.
     ============================================================ */
  #masthead{display:none !important;}
  #colophon{display:none !important;}
  /* our footer reuses the demo's element-level footer styles */
  footer.ho-footer{margin-top:var(--space-1);}

  /* ============================================================
     Header — clean, conventional, logo-forward.
     Crisp SVG mark on the left, horizontal nav on the right.
     Fixed bar, transparent at top, blurred dark on scroll.
     ============================================================ */
  .nav__inner{height:52px;}
  .nav .mark img{height:24px !important;width:auto !important;max-width:none;opacity:1;}
  .footer__top .mark img{height:26px;width:auto;opacity:0.96;}

  @media (max-width:680px){
    .nav .mark img{height:24px;}
  }

  /* ============================================================
     Header wiring fixes: admin-bar offset + menu list reset.
     ============================================================ */
  .admin-bar .nav{top:32px;}
  @media screen and (max-width:782px){
    .admin-bar .nav{top:46px;}
  }
  /* wp_nav_menu lists shouldn't inherit default indent/margins */
  .nav__links,.footer__links{margin:0;padding:0;list-style:none;}

  /* ============================================================
     Bottom-align the wordmark + menu to the logo's baseline,
     so all three share one line with equal spacing to the
     header's bottom edge. Desktop only.
     ============================================================ */
  @media (min-width:681px){
    .nav__inner{
      box-sizing:border-box;
      align-items:flex-end;
      padding-bottom:14px;
    }
    .mark{align-items:flex-end;}
  }

  /* ============================================================
     Pre-handoff fixes: mobile hamburger, mobile dropdown, footer.
     ============================================================ */

  /* Kill iOS's default button chrome + blue tap-highlight on the toggle. */
  .nav__toggle{
    -webkit-appearance:none;
    appearance:none;
    padding:0;
    -webkit-tap-highlight-color:transparent;
    background:transparent;
  }

  @media (max-width:680px){
    /* Dropdown now matches the 52px header and hides completely
       (visibility prevents the last item peeking at the top). */
    .nav__links{
      top:52px;
      transform:translateY(-115%);
      visibility:hidden;
      transition:transform 0.35s var(--ease), visibility 0s linear 0.35s;
      /* restore side gutters killed by the desktop padding:0 reset */
      padding:var(--space-3) clamp(1.5rem, 6vw, 2.5rem) var(--space-4);
    }
    .nav__links.is-open{
      transform:translateY(0);
      visibility:visible;
      transition:transform 0.35s var(--ease);
    }
    /* When logged in, the admin bar pushes the header down 46px. */
    .admin-bar .nav__links{top:98px;}
  }

  /* Footer matches the header: same logo height, same bottom-aligned row. */
  .footer__top{align-items:flex-end;}
  .footer__top .mark img{height:24px;}

  /* ============================================================
     v0.22.0 — Home page (front page).
     The home page is built from Custom HTML blocks + the
     [ho_selected_work] shortcode, on a page set to Astra
     "Full Width / Stretched". These rules (a) let the hero bleed
     edge-to-edge by neutralising Astra's content-container padding
     on the front page only, and (b) offset in-page anchor jumps
     (#about, #tickets, #contact ...) so the fixed header doesn't
     cover the section heading.
     ============================================================ */

  /* Let the home sections span the viewport; each section's own
     .wrap still re-centres content to 1120px as designed. Scoped to
     .home so no other page/template is affected. */
  .home #content .ast-container{
    max-width:100%;
    padding-left:0;
    padding-right:0;
    display:block;
  }
  .home #primary,
  .home #content .site-content > .ast-container > #primary{
    margin:0;
    padding:0;
    max-width:none;
  }
  .home .entry-content,
  .home .ast-article-single{
    margin:0;
    padding:0;
  }
  /* The hero already clears the fixed header via its own padding;
     make sure nothing adds a stray top gap above it. */
  .home .entry-content > *:first-child{margin-top:0;}

  /* In-page anchor offset so headings clear the ~52px fixed header. */
  #home,#selected,#about,#tickets,#join,#contact{
    scroll-margin-top:64px;
  }
  .admin-bar #home,
  .admin-bar #selected,
  .admin-bar #about,
  .admin-bar #tickets,
  .admin-bar #join,
  .admin-bar #contact{
    scroll-margin-top:96px;
  }

  /* ============================================================
     v0.23.0 — Guarantee the dark canvas from the child theme.
     The near-black background must NOT depend on an Astra Customizer
     setting: Astra prints its colour CSS after this stylesheet, so a
     default/unset Site Background paints white over our body rule.
     Force the canvas here (and stop Astra's structural wrappers from
     laying a light surface over it) so every page sits on #0a0a0a.
     Raised surfaces (cards, browser panes) still set their own bg.
     ============================================================ */
  html{background-color:var(--bg);}
  body{background-color:var(--bg) !important;}

  #page,
  #content,
  .site-content,
  .ast-container,
  .ast-separate-container,
  .ast-narrow-container,
  .ast-plain-container,
  #primary,
  .entry-content,
  .ast-article-single,
  .ast-article-post{
    background:transparent !important;
  }

  /* ============================================================
     v0.24.0 — Home iteration: content links, header spacing, hero mark.
     ============================================================ */

  /* (#2) Astra styles links inside post content (.entry-content a) with an
     underline and its own link colour, which overrode our monochrome design
     on the home page — cards, eyebrows, titles and buttons all came out
     underlined and tinted. Re-assert the design within the home content only.
     Buttons keep their own colour; .text-link keeps its muted tone; the
     intended "underlines" on .text-link and the contact email are drawn with
     border-bottom, so they survive this reset. */
  .home .entry-content a{ text-decoration:none !important; }
  .home .entry-content a:not(.btn):not(.text-link){ color:inherit !important; }

  /* (#1) Header: a little more vertical room so the row never crowds the
     hairline that appears once the bar goes dark on scroll. Desktop only;
     the mobile bar and its dropdown offsets are unchanged. */
  @media (min-width:681px){
    .nav__inner{
      height:auto;
      min-height:58px;
      padding-top:14px;
      padding-bottom:16px;
    }
  }

  /* ============================================================
     v0.25.0 — Home polish: section width, membership card, header.
     ============================================================ */

  /* (#2) In the full-width content context the design's standard 1120px
     column (.wrap) was being allowed to stretch, so the home sections ran
     wider than the header/footer/projects. Re-assert the column for the
     post-hero sections so everything lines up with the header logo. The
     hero (#home) is deliberately left full-bleed-wide, as approved. */
  .home .entry-content .wrap{
    max-width:var(--maxw) !important;
    margin-left:auto !important;
    margin-right:auto !important;
  }
  .home #home .wrap{ max-width:none !important; }   /* hero stays wide */

  /* (#4) The membership teaser uses a single tier card. On its own (outside
     the 3-up grid that draws the dividing hairlines) it has no border and
     would stretch across the whole column — looking empty. Give it a real
     border and a natural card width, sat to the right of its column. */
  .home #join .tier-card{
    border:1px solid var(--border);
    max-width:420px;
    justify-self:end;
  }

  /* (#1) Header: centre the logo + menu in the bar with even space top and
     bottom, so the hairline that appears on scroll never crowds them.
     (Previously the row was bottom-aligned, which sat it against the line.) */
  @media (min-width:681px){
    .nav__inner{
      align-items:center;
      height:60px;
      min-height:0;
      padding-top:0;
      padding-bottom:0;
    }
    .mark{ align-items:center; }
  }

  /* Defensive: never let a stray top border appear under the fixed header
     on the home page. */
  .home #content,
  .home .ast-container,
  .home .entry-content,
  .home #primary{ border-top:0 !important; }

  /* ============================================================
     v0.26.0 — Home polish round 3.
     ============================================================ */

  /* (#1) The grey line under the header is the hero section's top divider:
     every <section> gets a border-top as the between-section divider, and
     the hero is the first section, so it shouldn't have one. Remove it. */
  .home #home{ border-top:none !important; }

  /* (#2) About & Tickets: vertically centre the spec card against the taller
     text column so the two sides read as aligned, not top-offset. */
  .home #about .about__grid,
  .home #tickets .about__grid{ align-items:center; }

  /* (#4) Membership teaser card: centre it against the pitch, let it size to
     its content (no stretched gap before the button), and add a quiet divider
     above the benefits for structure. */
  .home #join .form-section__grid{ align-items:center; }
  .home #join .tier-card__benefits{
    flex-grow:0;
    border-top:1px solid var(--border);
    margin-top:0.75rem;
    padding-top:1rem;
  }

  /* (#5) Contact: pull the footer up close — minimal space below the form. */
  .home #contact{ padding-bottom:var(--space-3); }

  /* ============================================================
     v0.27.0 — Button label centring, contact space.
     ============================================================ */

  /* (#3) Full-width CTAs ("Become a member — free", "Send message"): centre
     the label instead of leaving it left-aligned. */
  .home #join .tier-card .btn,
  .home #contact .form .btn{ justify-content:center; }

  /* (#4) Contact: ease the space below the form back to ~half of the prior
     reduction (was 8rem, then 1.5rem; now ~4.75rem). */
  .home #contact{ padding-bottom:4.75rem; }

  /* ============================================================
     v0.29.0 — Footer breathing room; top-gap trims (desktop + mobile);
     mobile section spacing; contact button alignment.
     ============================================================ */

  /* (#2) Footer: more space between the logo/menu row and its top hairline. */
  footer.ho-footer{ padding-top:var(--space-4); }

  /* (#4) Desktop: trim the at-the-top gap ~half on home (hero) and projects. */
  .hero{ padding-top:36px; }
  .projects-page{ padding-top:75px; }

  /* (#10) Contact "Send message": back to left-aligned label
     (the membership CTA stays centred). */
  .home #contact .form .btn{ justify-content:flex-start; }

  @media (max-width:680px){
    /* (#3) Mobile home hero: drop the full-height centring that created
       the tall empty gap under the header. */
    .hero{ min-height:auto; padding-top:5rem; padding-bottom:var(--space-6); }

    /* (#5) Mobile projects: trim the top gap ~half. */
    .projects-page{ padding-top:60px; }

    /* (#6,#7) Mobile About & Tickets: halve the gap between text and card. */
    .home #about .about__grid,
    .home #tickets .about__grid{ gap:2.75rem; }

    /* (#8) Mobile Membership: tighten the gap before the card to ~25%. */
    .home #join .form-section__grid{ gap:1.375rem; }

    /* (#9) Mobile Contact: halve the gap between text and form. */
    .home #contact .form-section__grid{ gap:2.75rem; }
  }

  /* ============================================================
     v0.35.0 — Standalone content pages (Membership, and any future
     Page that isn't the home front page). Mirrors the home page's
     full-width + link handling, but scoped to .page:not(.home) so the
     finished home rules stay untouched. The dark canvas, the
     .projects-page top gap, the tier-card styles and .btn--disabled
     already exist globally, so this block only fixes the Astra page
     wrappers (full-width container, zeroed margins, link reset, and the
     1120px column constraint).
     ============================================================ */

  .page:not(.home) #content .ast-container{
    max-width:100%;
    padding-left:0;
    padding-right:0;
    display:block;
  }
  .page:not(.home) #primary{
    margin:0;
    padding:0;
    max-width:none;
  }
  .page:not(.home) .entry-content,
  .page:not(.home) .ast-article-single{
    margin:0;
    padding:0;
  }
  .page:not(.home) .entry-content > *:first-child{ margin-top:0; }

  /* Astra underlines/tints links inside .entry-content; re-assert the
     monochrome design. Buttons and .text-link keep their own treatment. */
  .page:not(.home) .entry-content a{ text-decoration:none !important; }
  .page:not(.home) .entry-content a:not(.btn):not(.text-link){ color:inherit !important; }

  /* Keep the design's 1120px column from stretching in full-width context. */
  .page:not(.home) .entry-content .wrap{
    max-width:var(--maxw) !important;
    margin-left:auto !important;
    margin-right:auto !important;
  }

  /* ============================================================
     v0.36.0 — Two final tweaks.

     (1) HOME HERO: the logo image and the "CREATIVE STUDIO" eyebrow are
         removed (delete those two lines from BLOCK 1 — see chat). The
         backlight pool is killed too, so nothing glows. The hero is a
         full-height centered flexbox, so with those gone the <h1>
         re-centers and leads on its own — no empty gap, desktop or mobile.

     (2) PROJECTS BROWSER (mobile, List mode only): the top list box was a
         fixed 280px in every view, which left the detail/info panel cramped
         under tall List rows. Shorten the List-mode list box and clamp each
         row's summary to 2 lines, so the info panel below gets more room and
         shows more at a glance. Mosaic / Compact are untouched.
     ============================================================ */

  /* (1) Let the h1 sit flush now that it leads the hero. */
  .hero__inner > h1:first-child{ margin-top:0; }

  /* (2) Mobile List-mode: shorter picker box, taller info panel. */
  @media (max-width:860px){
    .browser__list.mode-list{ max-height:200px; }
    .browser__list.mode-list .list-item{ padding:0.7rem 1rem; }
    .browser__list.mode-list .list-item__summary{
      display:-webkit-box;
      -webkit-line-clamp:2;
      -webkit-box-orient:vertical;
      overflow:hidden;
    }
  }

  /* ============================================================
     v0.37.0 — Mobile home hero: breathing room above the headline.
     With the logo removed, the <h1> sat right under the header. Raise ONLY
     the hero's top padding for a comfortable gap below the header; the
     bottom padding (the space below the content the owner wants kept) is
     left exactly as-is.
     ============================================================ */
  @media (max-width:680px){
    .hero{ padding-top:7.5rem; }
  }

  /* ============================================================
     v0.38.0 — Hero top padding, per request.
     Desktop: the hero text was vertically centred in the full-height
     section, so there was no fixed header-to-text gap. Anchor the content
     to the top with the same 7.5rem gap mobile uses. The skyline stays
     pinned to the hero's bottom (it's absolutely positioned), so the lower
     hero still reads as composed, not empty.
     Mobile: double the top gap to 15rem. Bottom spacing left as-is.
     (If the desktop top-anchor isn't the look you want, the alternative is
     to keep it centred and just nudge it down — easy switch.)
     ============================================================ */
  .hero{ align-items:flex-start; padding-top:7.5rem; }

  @media (max-width:680px){
    .hero{ padding-top:15rem; }
  }

  /* ============================================================
     v0.39.0 — Fix the desktop hero spacing regression from v0.38.0.
     v0.38.0 top-anchored the hero text (align-items:flex-start), which on
     desktop actually pulled it UP to 7.5rem from the top — LESS room than
     the previous vertical centring, so the gap looked like it vanished.
     Restore centring; the 7.5rem top padding (kept) now biases the centred
     block slightly lower, giving a touch more header-to-text space than the
     original. Mobile (min-height:auto) is unaffected by centring and keeps
     its 15rem top gap.
     ============================================================ */
  .hero{ align-items:center; }

  /* ============================================================
     v0.40.0 — Mobile hero spacing tune (desktop untouched).
     Top gap: 15rem -> 12rem (-20%, header to headline).
     Bottom gap: --space-6 -> +50% (buttons to the banner's bottom border).
     ============================================================ */
  @media (max-width:680px){
    .hero{
      padding-top:12rem;
      padding-bottom:calc(var(--space-6) * 1.5);
    }
  }

  /* ============================================================
     v0.41.0 — Projects browser, mobile: full-height info + roomy List.
     Root cause of the cramped/scrolling info panel: .browser__detail uses
     flex-basis:0 + overflow-y:auto. That's correct for the DESKTOP
     side-by-side layout (fill remaining width, scroll vertically inside the
     fixed-height browser), but on mobile the browser is a vertical column,
     where flex-basis:0 collapses the panel and overflow-y:auto then clips +
     scrolls its content. Override on mobile so the panel sizes to its
     content and shows everything — no inner scroll, any view mode.
     Because the panel no longer competes for space, restore the List-mode
     picker to its original 280px (shows 3+) — undoing the v0.36.0 squeeze.
     Mosaic / Compact and desktop are untouched.
     ============================================================ */
  @media (max-width:860px){
    .browser__detail{
      flex:0 0 auto;     /* was grow:1 / basis:0 — let it size to content */
      height:auto;
      overflow:visible;  /* was overflow-y:auto — no clipping/scroll here */
    }
    .browser__list.mode-list{ max-height:280px; }  /* restore roomy picker */
  }

  /* ============================================================
     v0.42.0 — Projects browser polish.
     (a) Mobile (<=680px): give the SELECTION list (picker) a sensible
         height per view mode so it shows a set number of items and scrolls
         past that. Tuned for typical phone widths.
           List    ~2.7-3 items                 -> 230px
           Mosaic  ~9 items (3 rows, 3-col grid)-> calc(100vw - 2.5rem)
           Compact ~5 items                     -> 215px
         Mosaic tracks viewport width because its square tiles scale with it.
     (b) Replace the OS default scrollbars on the browser panes (the "ugly"
         ones on desktop) with thin, monochrome, on-brand scrollbars.
     ============================================================ */
  @media (max-width:680px){
    .browser__list.mode-list{    max-height:230px; }
    .browser__list.mode-mosaic{  max-height:calc(100vw - 2.5rem); }
    .browser__list.mode-compact{ max-height:215px; }
  }

  /* (b) On-brand scrollbars for the browser panes (desktop + mobile picker). */
  .browser__list,
  .browser__detail{
    scrollbar-width:thin;                              /* Firefox */
    scrollbar-color:var(--border-strong) transparent;  /* thumb / track (FF) */
  }
  .browser__list::-webkit-scrollbar,
  .browser__detail::-webkit-scrollbar{ width:8px; height:8px; }
  .browser__list::-webkit-scrollbar-track,
  .browser__detail::-webkit-scrollbar-track{ background:transparent; }
  .browser__list::-webkit-scrollbar-thumb,
  .browser__detail::-webkit-scrollbar-thumb{
    background:var(--border-strong);
    border-radius:4px;
  }
  .browser__list::-webkit-scrollbar-thumb:hover,
  .browser__detail::-webkit-scrollbar-thumb:hover{ background:var(--text-faint); }

  /* ============================================================
     v0.43.0 — Home "Selected work" cards now show the project's featured
     image as a full-bleed thumbnail at the top of each card. The renderer
     only outputs .card__thumb when the project has a featured image, so
     image-less projects degrade gracefully to the original text card.
     ============================================================ */
  .card__thumb{
    display:block;
    /* bleed to the card's top + side edges (cancel the card's space-4 pad) */
    margin:calc(var(--space-4) * -1) calc(var(--space-4) * -1) 0;
    aspect-ratio:16 / 9;
    overflow:hidden;
    background:var(--bg-raised);
    border-bottom:1px solid var(--border);
  }
  .card__thumb img{
    display:block;
    width:100%;
    height:100%;
    object-fit:cover;
    transition:transform 0.4s var(--ease);
  }
  .card:hover .card__thumb img{ transform:scale(1.03); }

  /* ============================================================
     v0.52.0 — Mosaic squares: the fix (verified in a real browser),
     and consolidation of the v0.44–v0.49 experiments.

     ROOT CAUSE (confirmed by measuring resolved track sizes in
     Chromium, not guessed): inside the fixed-height, overflow:auto
     projects list, CSS GRID auto-row sizing never picked up each
     tile's intrinsic square height. The row track resolved to the
     placeholder's ~150px default height regardless of the (correct)
     ~178px column width, so every row was ~24px too short and the
     next row overlapped the previous one's bottom — exactly the
     reported symptom, and worse when narrower. Clicking a tile forced
     a reflow that recomputed the rows correctly, which is why the
     squares "snapped" right on click. None of aspect-ratio,
     padding-top:100%, an in-flow <svg> sizer, or a container-query
     (100cqi) ::before spacer feeds a GRID ROW's auto height reliably
     in that container; each was tried in v0.44–v0.49 and each was
     correct only AFTER a reflow.

     FIX: stop using CSS grid for the mosaic and lay the tiles out with
     FLEXBOX (flex-wrap). Flex has no row tracks, so a wrapped line's
     height is simply the tile's own box height — and aspect-ratio:1
     DOES make a square box in flex. Each tile's width is a
     deterministic flex-basis = (100% - gaps) / columns, so the
     squares are exact and correct on first paint, no reflow needed.
     This block fully replaces the stacked grid experiments above.

     (The .mosaic-item__sizer <svg> is no longer needed; it has been
     removed from projects-browser.js and is also hidden here so a
     cached older script can't reintroduce a stray box.)
     ============================================================ */
  .browser__list.mode-mosaic{
    display:flex;
    flex-wrap:wrap;
    gap:1px;
    background:var(--border);
    padding:1px;
    align-content:flex-start;
  }
  .mosaic-item{
    flex:0 0 calc((100% - 1px) / 2);   /* 2 columns; one 1px gap between them */
    aspect-ratio:1;                     /* square — works in flex (grid rows ignore it) */
    position:relative;
    overflow:hidden;
  }
  .mosaic-item__img{
    position:absolute; inset:0;
    width:100%; height:100%;
    object-fit:cover; display:block;
  }
  .mosaic-item__icon{
    position:absolute; inset:0;
    width:auto;                          /* override base .mosaic-item__icon width:36% */
    display:flex; align-items:center; justify-content:center;
  }
  .mosaic-item__icon svg{ width:36%; height:auto; }  /* override base svg width:100% */

  @media (max-width:860px){
    .mosaic-item{ flex-basis:calc((100% - 2px) / 3); }   /* 3 columns; two 1px gaps */
  }

  /* --- preserved from the old blocks (unchanged behavior) --- */

  /* Mobile mosaic picker height (carried over from v0.45). NOTE: this was
     tuned while the tiles were collapsing; now that rows are true squares the
     visible row count may differ — confirm with a phone screenshot and retune
     this single value if needed. */
  @media (max-width:680px){
    .browser__list.mode-mosaic{ max-height:calc((100vw - 2.5rem) * 0.81); }
  }

  /* Home "Selected work" thumbnail: bottom-edge fade as a cropped-preview cue
     (carried over verbatim from v0.45), with centre crop (v0.48 revert). */
  .card__thumb{
    position:relative;
    border-bottom:none;
  }
  .card__thumb::after{
    content:"";
    position:absolute;
    left:0; right:0; bottom:0;
    height:35%;
    background:linear-gradient(to bottom, transparent, var(--bg) 96%);
    pointer-events:none;
    transition:background 0.3s var(--ease);
  }
  .card:hover .card__thumb::after{
    background:linear-gradient(to bottom, transparent, var(--bg-raised) 96%);
  }
  .card__thumb img{ object-position:center; }

  /* ============================================================
     v0.53.0 — Mosaic tile titles.
     Show each project's title inside its mosaic square, legible over
     BOTH a featured image and the near-black skyline placeholder,
     graceful with long titles and small tiles, and consistent with the
     title styling used elsewhere (Space Grotesk, monochrome — no new
     style introduced).

     Two independent layers so the scrim auto-darkens images while the
     two-line clamp stays reliable (a single element with a big top pad
     let a clipped 3rd line peek through):
       - .mosaic-item::after  -> a bottom gradient scrim, the same
         near-black as the canvas: invisible over the placeholder, just
         darkens the foot of an image enough to guarantee contrast.
       - .mosaic-item__label  -> the title text, clamped to two lines
         with an ellipsis so it can never overflow the square.
     Both ignore pointer events so the whole tile stays clickable.
     (Overrides the legacy base .mosaic-item__label rule, incl. its
     mono font and its own gradient.)
     ============================================================ */
  .mosaic-item::after{
    content:"";
    position:absolute;
    left:0; right:0; bottom:0;
    height:58%;
    background:linear-gradient(to top,
                rgba(10,10,10,0.92) 0%,
                rgba(10,10,10,0.55) 45%,
                rgba(10,10,10,0) 100%);
    pointer-events:none;
    z-index:1;
  }
  .mosaic-item__label{
    position:absolute;
    left:0.75rem; right:0.75rem; bottom:0.6rem;   /* inset via position, not padding */
    z-index:2;
    margin:0;
    padding:0;                       /* padding lets line-clamp bleed a 3rd line; keep it 0 */
    background:none;                 /* the scrim is the ::after layer, not here */
    font-family:var(--font-display);
    font-weight:500;
    font-size:0.82rem;
    line-height:1.25;
    letter-spacing:0;
    color:var(--text);
    text-shadow:0 1px 2px rgba(0,0,0,0.45);   /* extra legibility over busy images */
    pointer-events:none;
    display:-webkit-box;             /* reliable 2-line clamp + ellipsis */
    -webkit-box-orient:vertical;
    -webkit-line-clamp:2;
    overflow:hidden;
  }
  @media (max-width:860px){
    .mosaic-item::after{ height:62%; }
    .mosaic-item__label{ left:0.6rem; right:0.6rem; bottom:0.5rem; font-size:0.74rem; }
  }

  /* ============================================================
     v0.54.0 — Home membership teaser: mute the intro paragraph.
     The descriptive <p> in the #join teaser is classless, so it
     inherited the near-white body color, while the equivalent copy
     in #about/#tickets (.about__body) and #contact (inline style)
     is muted grey. This brings it in line with the rest of the body
     text. Scoped to that one classless paragraph, so the eyebrow,
     the heading, and the tier card keep their own colors.
     ============================================================ */
  .home #join p:not([class]){ color:var(--text-muted); }

  /* ============================================================
     v0.55.0 — Subtle depth for the home About / Tickets / Membership.
     The two spec sheets and the membership tier card were 1px outlines
     on the same near-black as the page, so they read as flat. Lift them
     onto the design system's raised surface (--bg-raised) with a faint
     lit top edge and a soft ambient shadow, so each panel sits just
     above the page. Monochrome, no color, no layout change; scoped to
     the home panels (the standalone membership-page cards are left as-is).
     ============================================================ */
  .spec,
  .home #join .tier-card{
    background:var(--bg-raised);
    box-shadow:
      inset 0 1px 0 rgba(242,242,237,0.05),   /* faint lit top edge */
      0 10px 28px -18px rgba(0,0,0,0.85);       /* soft ambient grounding */
  }

  /* ============================================================
     v0.56.0 — Home spacing tweaks (About / Tickets / Membership / Contact).
     DESKTOP (>=861px):
       - #join: double the space below the "Become a member" heading
         (it was flush — only line-leading separated it from the copy).
       - Push the right-hand cards down off-centre by different amounts,
         leaving the LEFT TEXT COLUMNS untouched. Uses position:relative +
         top (NOT transform — the cards are .reveal elements whose entrance
         animation already uses transform, so a transform here would clash):
           About      — a little       (top:24px)
           Membership — moderately      (top:46px)
           Tickets    — significantly   (top:72px)
     MOBILE (<=680px, where the columns stack):
       - About & Tickets: halve the gap between the text and the card
         (2.75rem -> 1.375rem).
       - Contact: ease the gap between the copy and the form ~20%
         (2.75rem -> 2.2rem).
     ============================================================ */
  @media (min-width:861px){
    .home #join h2{ margin-bottom:0.75rem; }
    .home #about .spec{ position:relative; top:24px; }
    .home #tickets .about__grid > div:last-child{ position:relative; top:72px; }
    .home #join .tier-card{ position:relative; top:46px; }
  }
  @media (max-width:680px){
    .home #about .about__grid,
    .home #tickets .about__grid{ gap:1.375rem; }
    .home #contact .form-section__grid{ gap:2.2rem; }
  }

  /* ============================================================
     v0.57.0 — Tickets: +20% space between the "Get tickets" button and the
     section divider line below it (desktop only). The card+button were
     shifted down 72px in v0.56, leaving a 56px gap to the line; add ~11px
     (~20% of 56px) to the section's bottom padding to open it back up.
     ============================================================ */
  @media (min-width:861px){
    .home #tickets{ padding-bottom:calc(var(--space-7) + 0.7rem); }
  }

  /* ============================================================
     v0.58.0 — "In Progress" status page (/in-progress/).
     A standalone, centered placeholder page used as the graceful
     destination for any link/button that isn't live yet (ticket
     checkout before an event is scheduled, Partnership CTAs, future
     pages, etc.). Mirrors the home hero: the faint skyline motif sits
     behind centered, monochrome content. Scoped entirely to
     .status-page so it can never affect the home or any other page.
     No new tokens, no color, no JS changes (reuses #skyline + .reveal,
     both already handled site-wide by nav.js).
     ============================================================ */
  .status-page{
    position:relative;
    min-height:100vh;
    display:flex;
    align-items:center;
    justify-content:center;
    text-align:center;
    padding:72px 0 var(--space-6);
    overflow:hidden;
  }

  .status-page__inner{
    position:relative;
    z-index:2;
    max-width:640px;
    margin:0 auto;
    padding-left:clamp(1.25rem, 4vw, 3rem);
    padding-right:clamp(1.25rem, 4vw, 3rem);
  }

  .status-page .eyebrow{
    justify-content:center;
    margin-bottom:var(--space-3);
  }

  .status-page h1{
    font-family:var(--font-display);
    font-weight:600;
    font-size:clamp(2.2rem, 5vw, 3.6rem);
    line-height:1.06;
    letter-spacing:-0.01em;
    margin-bottom:var(--space-3);
  }

  .status-page__lede{
    color:var(--text-muted);
    font-size:clamp(1rem, 1.5vw, 1.12rem);
    max-width:460px;
    margin:0 auto var(--space-4);
  }

  .status-page__actions{
    display:flex;
    gap:1rem;
    justify-content:center;
    flex-wrap:wrap;
  }

  @media (max-width:680px){
    .status-page{
      min-height:calc(100vh - 56px);
      padding:96px 0 var(--space-5);
    }
  }

  /* ============================================================
     v0.59.0 — Tickets: live / closed toggle.
     The #tickets section now carries BOTH states in the Home block:
       .ho-tickets-active  — the live event (details + Eventbrite button)
       .ho-tickets-closed  — the "no event scheduled" message (default)
     Which one shows is driven by a Customizer switch
     (Appearance > Customize > "House Ozaki — Tickets" > "An event is
     currently live"). functions.php adds the body class .ho-tickets-live
     when that switch is ON. Default (switch OFF) shows the closed state.
     ============================================================ */
  .ho-tickets-active{ display:none; }
  .ho-tickets-closed{ display:block; }
  body.ho-tickets-live .ho-tickets-active{ display:block; }
  body.ho-tickets-live .ho-tickets-closed{ display:none; }

  /* The closed state's right column is a single CTA, not a tall spec
     sheet, so drop the v0.56 downward nudge and let it centre cleanly. */
  @media (min-width:861px){
    .home #tickets .ho-tickets-closed .about__grid > div:last-child{ top:0; }
  }

  /* ============================================================
     v0.60.0 — Contact form (Contact Form 7) integration.
     The #contact section is now rendered by [ho_contact form="ID"]
     (inc/home-helpers.php), which injects a CF7 form carrying the site's
     own .form classes. These rules let CF7's wrapper + field markup sit
     transparently inside the existing .form styling, render its
     response / validation messages in the monochrome system (no red),
     and hide the self-contained honeypot decoy field.
     ============================================================ */
  #contact .wpcf7{ width:100%; }
  .form .wpcf7-form-control-wrap{ display:block; }
  .form .wpcf7-form-control-wrap input,
  .form .wpcf7-form-control-wrap textarea{ width:100%; }

  .form .wpcf7-response-output{
    margin:0.6rem 0 0;
    padding:0.6rem 0 0;
    border:0;
    font-family:var(--font-mono);
    font-size:0.8rem;
    line-height:1.5;
    color:var(--text-muted);
  }
  .form.sent .wpcf7-response-output{ color:var(--text); }

  .form .wpcf7-not-valid-tip{
    font-family:var(--font-mono);
    font-size:0.72rem;
    letter-spacing:0.04em;
    color:var(--text-muted);
    margin-top:0.35rem;
  }
  .form .wpcf7-not-valid{ border-color:var(--border-strong); }
  .form .wpcf7-spinner{ margin:0 0 0 0.5rem; }

  /* Self-contained honeypot — off-screen and unfocusable. */
  .ho-hp{
    position:absolute;
    left:-9999px;
    width:1px;
    height:1px;
    overflow:hidden;
  }

  /* ============================================================
     v0.61.0 — Membership signup form (Zoho Campaigns embed).
     The /membership/ page embeds the Zoho double-opt-in signup form
     inside a Custom HTML block wrapped by #join-form. Zoho ships the
     form with a light-grey outer frame, a template background image,
     and a grey submit button. These rules neutralise the grey frame
     so the form blends into the page black, drop the template image,
     put the heading in the display font, and restyle the submit
     button to the site's off-white solid. Scoped to Zoho's stable
     element IDs (this form only appears on the membership page).
     The form body is already rgb(10,10,10) = our --bg, so it merges
     with the page on its own.
     ============================================================ */
  .join-form{
    max-width:600px;
    margin:var(--space-6) auto 0;
    padding:0 clamp(1.25rem, 4vw, 3rem);
    scroll-margin-top:100px;   /* clears the fixed nav when scrolled to */
  }

  #SIGNUP_PAGE{
    background-color:transparent !important;
    padding-top:0 !important;
  }
  #SIGNUP_BODY_ALL{ background-image:none !important; }
  #SIGNUP_HEADING{
    font-family:var(--font-display) !important;
    font-weight:600 !important;
  }
  #zcWebOptin{
    background-color:#f2f2ed !important;
    color:#0a0a0a !important;
    border:1px solid #f2f2ed !important;
    border-radius:3px !important;
    font-family:var(--font-body) !important;
    font-size:0.9rem !important;
    height:auto !important;
    padding:0.85rem 1.5rem !important;
  }

  /* ============================================================
     v0.62.0 — Tighter gap between the membership tier cards and the
     signup form (was --space-6, now --space-5) so they read as one
     section. (A "check your inbox/spam" helper note that originally
     shipped in v0.62/v0.63 was reverted in v0.64 — see below.)
     ============================================================ */
  .join-form{ margin-top:var(--space-5); }

  /* ============================================================
     v0.64.0 — Reverted the membership signup helper note. Removed the
     .join-form__note styling and the #privacyNotes height override
     from v0.62/v0.63; the Zoho thank-you page now carries the
     "check your inbox/spam" message instead. The on-page <p> line is
     deleted from the join-form block. The tighter card→form gap from
     v0.62 is kept.
     ============================================================ */

  /* ============================================================
     v0.65.0 — Footer legal links (Privacy Policy + Terms of Use).
     Added to the footer__bottom row (functions.php) beside the
     copyright line; space-between pushes them to the right. Styled
     to match the footer's muted links — they inherit the bottom
     row's mono / small size.
     ============================================================ */
  .footer__legal{
    display:flex;
    gap:1.25rem;
    flex-wrap:wrap;
  }
  .footer__legal a{
    color:var(--text-muted);
    transition:color 0.25s var(--ease);
  }
  .footer__legal a:hover{ color:var(--text); }

  /* ============================================================
     v0.66.0 — Fix mobile horizontal scroll on /membership/.
     The Zoho signup embed ships a fixed 600px-wide logo container
     (#imgBlock), plus a couple of min-widths, that push wider than a
     phone screen — letting the page scroll sideways into empty space
     and pinch-zoom out (Home/Projects don't, because their content
     fits). Clamp the Zoho form's fixed widths to the viewport, and
     also lock overflow-x on <html> (body already had it) so nothing
     on any page can force a sideways scroll.
     ============================================================ */
  html{ overflow-x:hidden; }
  #imgBlock{ width:100% !important; max-width:100% !important; }
  #recapDiv{ max-width:100% !important; }
  #SIGNUP_PAGE{ min-width:0 !important; max-width:100% !important; }
  #SIGNUP_BODY_ALL{ min-width:0 !important; max-width:100% !important; }

  /* ============================================================
     v0.69.0 — Tickets closed state: push CTA button down so it
     reads as vertically centred against the left-column content
     (divider bars + eyebrow + status + lede + body). The v0.59.0
     rule reset the right column to top:0; this overrides it to
     ~100px — matching the visual weight of the copy block.
     Desktop only; the grid stacks on mobile so top has no effect.
     ============================================================ */
  @media (min-width:861px){
    .home #tickets .ho-tickets-closed .about__grid > div:last-child{ top:100px; }
  }

  /* ============================================================
     v0.70.0 — Spec rows: two-column grid on mobile.
     The new About spec values (Discipline, Scope) are longer
     than the originals. With justify-content:space-between on
     a narrow screen, wrapped value text bleeds back to the
     row's left edge — orphaning words like "Technology" and
     "landmark" far from their value column.
     Fix: switch to grid(auto 1fr) on mobile. The label takes
     its natural width; the value gets the remainder and wraps
     cleanly inside its own column, anchored right of the label.
     Desktop (>680px) is completely untouched.
     ============================================================ */
  @media (max-width:680px){
    .spec__row{
      display:grid;
      grid-template-columns:auto 1fr;
      gap:0 1rem;
      align-items:start;
    }
  }

  /* ============================================================
     v0.71.0 — Spec rows mobile: revert v0.70.0 grid + right-
     align values. The grid approach is reverted (display:flex
     reinstated). Fix is simpler: text-align:right on the value
     span so any wrapped lines stay anchored to the right edge.
     Label stays left, value stays right — even when it wraps.
     ============================================================ */
  @media (max-width:680px){
    .spec__row{ display:flex; }
    .spec__row span:last-child{ text-align:right; }
  }

  /* ============================================================
     v0.72.0 — Tickets closed CTA: pull button up from 100px
     to 60px so it sits in the whitespace between the lede and
     the body paragraph rather than at body-text level.
     ============================================================ */
  @media (min-width:861px){
    .home #tickets .ho-tickets-closed .about__grid > div:last-child{ top:60px; }
  }

  /* v0.73.0 — Tickets closed CTA: pull up further to top:25px */
  @media (min-width:861px){
    .home #tickets .ho-tickets-closed .about__grid > div:last-child{ top:25px; }
  }

  /* ============================================================
     v0.75.0 — Project detail panel: readability fixes.

     1. Colors: .detail-title had no color so Astra's orange bled
        through. .card__meta / .card__date lost their token colors
        to Astra's heading specificity. Scoping all three under
        .browser__detail beats Astra without !important.

     2. Paragraph spacing: 1rem margin was fine for real paragraphs
        but the current content has every sentence as its own WP
        paragraph block — 1rem creates huge gaps between 2-word
        lines. Tightened to 0.5rem; owner should also merge related
        sentences inside WP editor into real paragraphs.
     ============================================================ */
  .browser__detail .detail-title{ color:var(--text); }
  .browser__detail .card__meta{ color:var(--text-faint); }
  .browser__detail .card__date{ color:var(--text-muted); }
  .detail-body p{ margin-bottom:0.5rem; }
  .detail-body p:last-child{ margin-bottom:0; }

  /* ============================================================
     v0.76.0 — Project detail: image at 80% width, left-aligned.
     Overrides the existing max-width:560px (higher specificity)
     so the image is always 80% of the detail panel regardless
     of screen width. Left-aligned by default (block element,
     no margin:auto). Detail panel only — no other images affected.
     ============================================================ */
  .browser__detail .detail-media{
    width:80%;
    max-width:none;
  }

  /* v0.77.0 — Detail image: double bottom margin (1.25rem → 2.5rem) */
  .browser__detail .detail-media{ margin-bottom:2.5rem; }

  /* v0.78.0 — Detail actions: double top margin (1.25rem → 2.5rem) */
  .browser__detail .detail-actions{ margin-top:2.5rem; }

  /* ============================================================
     v0.79.0 — Hero: center paragraph and buttons.
     The h1 now spans nearly full width so alignment is neutral,
     but the paragraph (max-width:480px) and action buttons were
     left-anchored. Center all three.
     ============================================================ */
  .hero__inner{ text-align:center; }
  .hero p{ margin-left:auto; margin-right:auto; }
  .hero__actions{ justify-content:center; }

  /* ============================================================
     v0.80.0 — Four fixes.

     1. Hero h1: lighter weight on desktop (600 → 300). The wide
        all-caps title at 600 reads heavy at large sizes; 300 gives
        the thin/elegant feel without changing mobile (which already
        looks good at its smaller clamp size).

     2. Tickets closed CTA: push button to the right edge of its
        340px grid column, aligning with the About spec card's
        right edge. Uses flex + justify-content:flex-end on the
        right column div — desktop only, same breakpoint as the grid.

     3 & 4 are PHP changes in home-helpers.php (no CSS needed).
     ============================================================ */
  @media (min-width:861px){
    .hero h1{ font-weight:300; }
    .home #tickets .ho-tickets-closed .about__grid > div:last-child{
      display:flex;
      justify-content:flex-end;
      align-items:flex-start;
    }
  }

  /* ============================================================
     v0.81.0 — Hero paragraph: wider on desktop only.
     480px → 800px at ≥861px so the text wraps to ~2 lines
     instead of 4-5. Centered via the existing margin:auto
     from v0.79.0. Mobile (≤860px) stays at 480px.
     ============================================================ */
  @media (min-width:861px){
    .hero p{ max-width:800px; }
  }

  /* ============================================================
     v0.82.0 — Anchor scroll offset: increase scroll-margin-top.
     The existing values (64px / 96px) were too small — the Astra
     sticky nav is ~85px tall, plus the 32px WP admin bar when
     logged in. Sections were sliding under the nav on click.
     Bumped to 90px (live visitors) / 125px (logged-in admin bar).
     ============================================================ */
  #home,#selected,#about,#tickets,#join,#contact{
    scroll-margin-top:90px;
  }
  .admin-bar #home,
  .admin-bar #selected,
  .admin-bar #about,
  .admin-bar #tickets,
  .admin-bar #join,
  .admin-bar #contact{
    scroll-margin-top:125px;
  }

  /* ============================================================
     v0.85.0 — Contact form: match membership card right edge.
     The CF7 form inherits .form { max-width:420px } which stops
     it short of the column's right edge. The tier-card in #join
     has no such cap and fills its full 1fr column. Removing the
     420px cap from the contact form's .wpcf7 lets it fill the
     same column width so both right edges land at the same point.
     ============================================================ */
  .home #contact .wpcf7{ max-width:100%; }

  /* ============================================================
     v0.86.0 — Contact form: force full column width at all layers.
     v0.85.0 targeted only the outer .wpcf7 wrapper. CF7 renders
     an inner <form.wpcf7-form> which Astra or CF7 may constrain
     separately. Hit all three layers (wrapper, form, inputs) and
     use !important to cut through anything Astra overrides.
     ============================================================ */
  .home #contact .wpcf7,
  .home #contact .wpcf7 form.wpcf7-form{
    width:100% !important;
    max-width:none !important;
    box-sizing:border-box !important;
  }

  /* ============================================================
     v0.87.0 — Equalize contact and membership grid columns.
     The #contact and #join grids are both 1fr 1fr but something
     (likely Astra) is applying a different effective column-gap
     to #contact, making its right column wider than #join's.
     Force the same column-gap on both so both right columns are
     identical width — left and right edges then align perfectly.
     ============================================================ */
  .home #join .form-section__grid,
  .home #contact .form-section__grid{
    column-gap:var(--space-6) !important;
  }

  /* ============================================================
     v0.88.0 — Contact form: inset left edge to match card padding.
     Both form and card fill the same column. The card has
     padding:var(--space-4) on all sides, so its visible content
     starts 36px inward from the column edge. The form inputs
     were flush with the column edge — 36px too far left.
     padding-left:var(--space-4) with box-sizing:border-box
     (set in v0.86.0) insets the inputs without growing the outer
     box, so left edges now match while right edge stays aligned.
     ============================================================ */
  .home #contact .wpcf7{
    padding-left:var(--space-4) !important;
  }

  /* ============================================================
     v0.90.0 — Housekeeping. No visual change, by design.

     Removed CSS that no longer matched anything in the markup.
     Every selector deleted here was verified in a real browser to
     match ZERO elements across Home, Projects, Membership and
     In Progress, at both desktop and mobile widths, in both the
     tickets-closed and tickets-live states. All 906 elements were
     compared property-by-property before and after: identical.

     REMOVED
     - The hero-logo chain (v0.24, v0.26-v0.28, v0.29 mobile, and
       the whole v0.30-v0.34 glow/bokeh/mask experiment, 236 lines).
       The hero logo itself was deleted from the markup back in
       v0.36, so none of it had applied since. This also retires the
       ho-logo-glow / ho-logo-glow2 / ho-bokeh / ho-shine /
       ho-glow-pool keyframes and the .hero__inner::before backlight
       layer, along with v0.36's now-pointless content:none killer.
       (.hero__inner keeps position:relative from the base rules.)
     - v0.68's .ho-status ticket indicator. The CSS shipped but its
       markup never did, so no dot or "Status:" label ever rendered.
     - v0.74's .about__hook / .about__manifesto / .about__statements
       editorial layout. Same story: styles only, never any markup.
       The live About section uses .about__grid, which is untouched.
     - Orphans from earlier revisions: .card--past, .card__price,
       .card__cta (the projects browser never emits them),
       .form__success / .form.is-submitted .form__fields (superseded
       by Contact Form 7 in v0.60), and the legacy
       .mosaic-item__sizer (retired by the v0.52 flexbox fix).

     If the ticket status light or the editorial About layout is
     wanted later, both are recoverable from the v0.89.0 zip.

     Also in this release (PHP, inc/home-helpers.php): the home
     "Selected work" trio now tops up to 3 cards when only one or
     two projects are ticked "Feature on home page", instead of
     leaving a lone card stranded in a three-column grid. With
     nothing ticked the behaviour is exactly as before.
     ============================================================ */

  /* ============================================================
     v0.91.0 — Production pass. Surface only: no content, no layout,
     no markup changes. Eight treatments, each in its own block below
     so any one can be lifted out on its own without disturbing the
     others. Reverting the whole release = re-upload v0.90.0.

       1. Film grain          6. Ambient light
       2. Unified imagery     7. Scroll progress
       3. Staggered entrance  8. Display type
       4. Hero wipe
       5. Card depth
     ============================================================ */

  /* ------------------------------------------------------------
     1. FILM GRAIN
     A fixed, non-interactive noise layer over the whole page, so
     the near-black ground reads as a photographed surface rather
     than a flat fill. pointer-events:none is load-bearing — the
     layer sits above the fixed header, so without it the whole
     site would be unclickable.
     ------------------------------------------------------------ */
  body::after{
    content:"";
    position:fixed;
    inset:0;
    z-index:9999;
    pointer-events:none;
    opacity:0.055;
    background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)' opacity='0.45'/%3E%3C/svg%3E");
  }
  /* The WP admin bar is a light UI element; keep the grain off it. */
  .admin-bar #wpadminbar{ z-index:100000; }

  /* ------------------------------------------------------------
     2. UNIFIED IMAGERY
     Project thumbnails arrive in unrelated styles (a monochrome
     contact sheet, a light poster, a colour illustration), so a row
     of them reads as three brands. Every BROWSING surface — home
     cards, browser list rows, mosaic tiles — gets one house
     treatment, and colour returns on hover/selection as a reward.
     The detail panel is deliberately exempt: once someone has opened
     a project, they should see it true.
     ------------------------------------------------------------ */
  .card__thumb img,
  .list-item__thumb--img img,
  .mosaic-item__img{
    filter:grayscale(1) contrast(1.16) brightness(0.86);
    transition:filter 0.5s var(--ease), transform 0.4s var(--ease);
  }
  .card:hover .card__thumb img,
  .list-item:hover .list-item__thumb--img img,
  .mosaic-item:hover .mosaic-item__img{
    filter:grayscale(0.55) contrast(1.06) brightness(0.98);
  }
  .list-item.is-selected .list-item__thumb--img img,
  .mosaic-item.is-selected .mosaic-item__img{
    filter:grayscale(0.25) contrast(1.03) brightness(1);
  }

  /* ------------------------------------------------------------
     3. STAGGERED ENTRANCE
     A .reveal container animates as one block, so a three-card grid
     arrives in a single slab. Here the container stops animating and
     hands the motion to its children, each offset by 90ms. Markup is
     untouched — the stagger keys off the .is-visible class nav.js
     already applies.

     Children rest at opacity:0 until .is-visible lands, exactly as
     .reveal already does. nav.js now guarantees that class even when
     IntersectionObserver is missing, so content cannot be stranded.
     ------------------------------------------------------------ */
  .projects__grid.reveal,
  .tier-grid.reveal,
  .spec.reveal{
    opacity:1;
    transform:none;
    transition:none;
  }
  .projects__grid.reveal > .card,
  .tier-grid.reveal > .tier-card,
  .spec.reveal > .spec__row{
    opacity:0;
  }
  .projects__grid.reveal.is-visible > .card,
  .tier-grid.reveal.is-visible > .tier-card,
  .spec.reveal.is-visible > .spec__row{
    animation:ho-stagger-in 0.8s var(--ease) both;
  }
  .projects__grid.reveal.is-visible > *:nth-child(1),
  .tier-grid.reveal.is-visible > *:nth-child(1),
  .spec.reveal.is-visible > *:nth-child(1){ animation-delay:0ms; }
  .projects__grid.reveal.is-visible > *:nth-child(2),
  .tier-grid.reveal.is-visible > *:nth-child(2),
  .spec.reveal.is-visible > *:nth-child(2){ animation-delay:90ms; }
  .projects__grid.reveal.is-visible > *:nth-child(3),
  .tier-grid.reveal.is-visible > *:nth-child(3),
  .spec.reveal.is-visible > *:nth-child(3){ animation-delay:180ms; }
  .projects__grid.reveal.is-visible > *:nth-child(4),
  .tier-grid.reveal.is-visible > *:nth-child(4),
  .spec.reveal.is-visible > *:nth-child(4){ animation-delay:270ms; }

  @keyframes ho-stagger-in{
    from{ opacity:0; transform:translateY(20px); }
    to  { opacity:1; transform:translateY(0); }
  }

  /* ------------------------------------------------------------
     4. HERO WIPE
     The headline rises out of a clipped edge instead of simply
     being there, with the paragraph and buttons following it in.
     This runs on page load, not on scroll — the hero is already in
     view. .hero__actions carries .reveal, which would hold it at
     opacity:0 until the observer fires, so it is released here and
     the load animation takes over.
     ------------------------------------------------------------ */
  .hero h1{ animation:ho-wipe-up 1.05s var(--ease) both; }
  .hero__inner > p{ animation:ho-wipe-up 1.05s var(--ease) 0.16s both; }
  .hero .hero__actions.reveal{
    opacity:1;
    transform:none;
    transition:none;
    animation:ho-wipe-up 1.05s var(--ease) 0.3s both;
  }
  @keyframes ho-wipe-up{
    from{ opacity:0; transform:translateY(0.5em); clip-path:inset(0 0 105% 0); }
    to  { opacity:1; transform:translateY(0);     clip-path:inset(0 0 -10% 0); }
  }

  /* ------------------------------------------------------------
     5. CARD DEPTH
     Home cards previously answered a hover with a background change
     only. Now they lift, take an ambient shadow, catch a lit top
     edge, and slide their arrow. The grid draws its dividers with a
     1px gap over a border-coloured background, so a lifted card
     needs z-index to sit above its neighbours' hairlines.
     ------------------------------------------------------------ */
  .projects__grid .card{
    transition:background-color 0.3s var(--ease),
               transform 0.4s var(--ease),
               box-shadow 0.4s var(--ease);
  }
  .projects__grid .card:hover{
    transform:translateY(-6px);
    box-shadow:0 26px 50px -30px rgba(0,0,0,0.95);
    z-index:3;
  }
  .projects__grid .card::before{
    content:"";
    position:absolute;
    inset:0;
    pointer-events:none;
    border-top:1px solid transparent;
    transition:border-color 0.4s var(--ease);
    z-index:4;
  }
  .projects__grid .card{ position:relative; }
  .projects__grid .card:hover::before{ border-top-color:rgba(242,242,237,0.42); }
  .card__link{ display:inline-flex; align-items:center; gap:0.4rem; }
  .card__link::after{
    content:"\2192";
    display:inline-block;
    transition:transform 0.3s var(--ease);
  }
  .card:hover .card__link::after{ transform:translateX(5px); }

  /* ------------------------------------------------------------
     6. AMBIENT LIGHT
     A faint pool of light at the top of each section gives the page
     an implied light source instead of a uniform flat ground. The
     hero is excluded — the skyline motif is already its atmosphere.
     Sections get position:relative so the layer can sit behind their
     content, and .wrap is lifted above it.
     ------------------------------------------------------------ */
  section{ position:relative; }
  section:not(.hero)::before{
    content:"";
    position:absolute;
    inset:0;
    pointer-events:none;
    z-index:0;
    /* The light's origin sits ABOVE the section (at 50% -14%), so the
       section's own top edge lands on the gradient's falloff rather than
       its peak. Peaking at the edge put a visible tonal step on every
       divider, and five of those down the page read as banding rather
       than as light. Wide and shallow is what makes it read as ambient. */
    background:radial-gradient(130% 78% at 50% -14%,
                rgba(242,242,237,0.05) 0%,
                rgba(242,242,237,0) 72%);
  }
  section > .wrap{ position:relative; z-index:1; }

  /* ------------------------------------------------------------
     7. SCROLL PROGRESS
     A hairline across the very top tracking position in the page.
     The element is created by nav.js, so no template or page markup
     changes. It clears the WP admin bar when logged in.
     ------------------------------------------------------------ */
  .ho-progress{
    position:fixed;
    top:0; left:0;
    height:2px;
    width:0;
    background:var(--text);
    z-index:10000;
    pointer-events:none;
    transition:width 0.12s linear;
  }
  .admin-bar .ho-progress{ top:32px; }
  @media screen and (max-width:782px){
    .admin-bar .ho-progress{ top:46px; }
  }

  /* ------------------------------------------------------------
     8. DISPLAY TYPE
     More distance between the largest and smallest type on the page
     is what reads as art-directed. The hero goes bigger and lighter
     at tighter leading, section headings tighten their tracking,
     and the mono eyebrows open up to hold their own against it.
     Scoped to the design's own components — the Privacy Policy and
     Terms pages are plain WordPress content and are left alone.
     ------------------------------------------------------------ */
  .hero h1{
    font-size:clamp(2.3rem, 7.4vw, 5.4rem);
    line-height:0.98;
    text-wrap:balance;
  }
  @media (min-width:861px){
    .hero h1{ font-weight:200; }
  }
  .section__head h2,
  .projects-page__head h2{
    font-size:clamp(1.9rem, 4.2vw, 3.1rem);
    letter-spacing:-0.022em;
    text-wrap:balance;
  }
  .about__lede,
  .status-page h1{ letter-spacing:-0.022em; text-wrap:balance; }
  .eyebrow{ letter-spacing:0.22em; }
  .card h3,
  .tier-card__name,
  .detail-title{ letter-spacing:-0.015em; }

  /* ------------------------------------------------------------
     Motion safety. The existing reduced-motion block neutralises
     transitions and releases .reveal; these additions cover the new
     keyframe animations and the stagger's resting state so nothing
     introduced above can hide content for those readers.
     ------------------------------------------------------------ */
  @media (prefers-reduced-motion:reduce){
    .hero h1,
    .hero__inner > p,
    .hero .hero__actions.reveal,
    .projects__grid.reveal > .card,
    .tier-grid.reveal > .tier-card,
    .spec.reveal > .spec__row{
      animation:none !important;
      opacity:1 !important;
      transform:none !important;
      clip-path:none !important;
    }
    .ho-progress{ transition:none; }
  }

  /* ============================================================
     v0.92.0 — Primary menu grows to eight items.

     About · Projects · Media · Partners · Membership · Tickets ·
     Talent · Contact. Media, Partners and Talent have no pages yet
     and point at /in-progress/ (see ho_nav_links() in functions.php).

     The row still fits on one line down to ~740px, but between the
     mobile breakpoint (680px) and ~740px the logo and the menu were
     closing to a 5px gap. Rather than move the breakpoint — which
     would mean re-cutting every other 680px rule in this file — the
     menu tightens its own spacing and type across that band only.
     Above 820px nothing changes.
     ============================================================ */
  @media (min-width:681px) and (max-width:820px){
    .nav__links{ gap:0.9rem; }
    .nav__links a{ font-size:0.78rem; }
  }

  /* The footer runs the same eight items and is free to wrap; give the
     wrapped rows a little vertical breathing room so two lines of links
     don't read as one dense block. */
  .footer__links{ row-gap:0.75rem; }

  /* ============================================================
     v0.93.0 — "Instrument".

     Brief: make the site read as cutting-edge / technical. The
     reference supplied was a neon-green terminal aesthetic; NONE of
     that palette is imported — the design system stays monochrome,
     as it always has. What is borrowed is the underlying reason that
     look feels advanced: the page behaves like a machine that is
     running. Here that comes from motion, light and technical
     vernacular rather than from colour.

       A. Blueprint substrate    E. Pointer-tracked card light
       B. Breathing skyline      F. Spec rows as telemetry
       C. Decoding headings      G. Footer system strip
       D. Registration brackets

     Every piece is additive. Removing this one block returns the site
     to v0.92.0 exactly; instrument.js is likewise self-contained.
     ============================================================ */

  /* ------------------------------------------------------------
     A. BLUEPRINT SUBSTRATE
     A hairline engineering grid beneath everything, masked so it is
     strongest at the top of the viewport and gone by the middle —
     present, never a pattern you actively read. body::after is
     already the film grain, so this takes body::before, and the two
     page wrappers are lifted above it.
     ------------------------------------------------------------ */
  body::before{
    content:"";
    position:fixed;
    inset:0;
    z-index:0;
    pointer-events:none;
    background-image:
      linear-gradient(to right,  rgba(242,242,237,0.030) 1px, transparent 1px),
      linear-gradient(to bottom, rgba(242,242,237,0.030) 1px, transparent 1px);
    background-size:72px 72px;
    -webkit-mask-image:radial-gradient(ellipse 130% 88% at 50% -6%, #000 18%, transparent 76%);
            mask-image:radial-gradient(ellipse 130% 88% at 50% -6%, #000 18%, transparent 76%);
  }
  #page{ position:relative; z-index:1; }
  footer.ho-footer{ position:relative; z-index:1; }

  /* ------------------------------------------------------------
     B. BREATHING SKYLINE
     The signature motif stops being a static chart. Once the bars
     have risen, each one drifts on its own slow cycle, like a level
     meter at idle. The delays start after the 1.1s rise so the
     entrance is never interrupted, and every bar gets a different
     period so the row never pulses in unison.
     ------------------------------------------------------------ */
  .skyline.is-visible span{
    animation:ho-idle 5.5s ease-in-out infinite alternate;
    animation-delay:1.2s;
  }
  @keyframes ho-idle{
    from{ transform:scaleY(0.93); }
    to  { transform:scaleY(1.07); }
  }
  .skyline.is-visible span:nth-child(3n+1){ animation-duration:6.4s; animation-delay:1.35s; }
  .skyline.is-visible span:nth-child(3n+2){ animation-duration:5.1s; animation-delay:1.6s; }
  .skyline.is-visible span:nth-child(4n)  { animation-duration:7.2s; animation-delay:1.25s; }
  .skyline.is-visible span:nth-child(5n)  { animation-duration:4.6s; animation-delay:1.75s; }

  /* ------------------------------------------------------------
     C. DECODING HEADINGS
     instrument.js swaps the characters; this only holds the line
     steady while it happens. Tabular figures and a fixed minimum
     height stop the scramble from reflowing the heading, which is
     what makes the effect read as a readout resolving rather than
     as text jittering.
     ------------------------------------------------------------ */
  .section__head h2,
  .projects-page__head h2,
  .status-page h1{
    font-variant-numeric:tabular-nums;
  }
  .is-decoding{
    opacity:0.92;
    font-variant-ligatures:none;
  }

  /* ------------------------------------------------------------
     D. REGISTRATION BRACKETS
     Four corner marks inset inside each project card — the
     vernacular of a technical drawing or a viewfinder. They fade up
     and draw inward on hover. Built from eight 1px gradients on one
     pseudo-element so no extra markup is needed.
     ------------------------------------------------------------ */
  .projects__grid .card::after{
    content:"";
    position:absolute;
    inset:16px;
    z-index:5;
    pointer-events:none;
    opacity:0;
    transition:opacity 0.4s var(--ease), inset 0.45s var(--ease);
    --c:rgba(242,242,237,0.55);
    background:
      linear-gradient(var(--c),var(--c)) left    top/11px 1px  no-repeat,
      linear-gradient(var(--c),var(--c)) left    top/1px  11px no-repeat,
      linear-gradient(var(--c),var(--c)) right   top/11px 1px  no-repeat,
      linear-gradient(var(--c),var(--c)) right   top/1px  11px no-repeat,
      linear-gradient(var(--c),var(--c)) left  bottom/11px 1px  no-repeat,
      linear-gradient(var(--c),var(--c)) left  bottom/1px  11px no-repeat,
      linear-gradient(var(--c),var(--c)) right bottom/11px 1px  no-repeat,
      linear-gradient(var(--c),var(--c)) right bottom/1px  11px no-repeat;
  }
  .projects__grid .card:hover::after{
    opacity:1;
    inset:11px;
  }

  /* ------------------------------------------------------------
     E. POINTER-TRACKED LIGHT
     v0.91 gave the card a lit top edge on hover; that same
     pseudo-element now also carries a soft pool of light that
     follows the cursor (instrument.js writes --mx / --my). The
     fallback centre keeps it sane before the first pointer event
     and on touch, where the script never binds.
     ------------------------------------------------------------ */
  .projects__grid .card{ --mx:50%; --my:30%; }
  .projects__grid .card::before{
    background:radial-gradient(260px circle at var(--mx) var(--my),
                rgba(242,242,237,0.075) 0%,
                rgba(242,242,237,0) 68%);
    opacity:0;
    transition:border-color 0.4s var(--ease), opacity 0.4s var(--ease);
  }
  .projects__grid .card:hover::before{ opacity:1; }

  /* ------------------------------------------------------------
     F. SPEC ROWS AS TELEMETRY
     A dotted leader runs from each label to its value, the way a
     schedule or an instrument legend sets them. The leader is a
     flex item created from the row's own ::before (ordered between
     the two spans), so again no markup changes. Suppressed below
     681px, where the values wrap onto their own lines and a leader
     would only add noise.
     ------------------------------------------------------------ */
  @media (min-width:681px){
    .spec__row{ align-items:baseline; }
    .spec__row span:first-child{ order:0; flex:0 0 auto; }
    .spec__row::before{
      content:"";
      order:1;
      flex:1 1 auto;
      min-width:20px;
      margin:0 0.75rem;
      border-bottom:1px dotted var(--border-strong);
      opacity:0.7;
      transform:translateY(-0.25em);
    }
    .spec__row span:last-child{ order:2; flex:0 1 auto; text-align:right; }
  }

  /* ------------------------------------------------------------
     G. FOOTER SYSTEM STRIP
     A single monospace line reporting something true: the site is
     up, and the current time. Sits above the existing copyright row.
     The dot pulses slowly so the strip reads as live rather than
     printed.
     ------------------------------------------------------------ */
  .ho-system{
    display:flex;
    align-items:center;
    gap:0.75rem;
    flex-wrap:wrap;
    padding:0.75rem 0;
    border-top:1px solid var(--border);
    font-family:var(--font-mono);
    font-size:0.68rem;
    letter-spacing:0.14em;
    text-transform:uppercase;
    color:var(--text-faint);
  }
  .ho-system__dot{
    width:6px;
    height:6px;
    border-radius:50%;
    background:var(--text-muted);
    flex-shrink:0;
    animation:ho-pulse 2.6s ease-in-out infinite;
  }
  @keyframes ho-pulse{
    0%,100%{ opacity:0.35; transform:scale(1); }
    50%    { opacity:1;    transform:scale(1.25); }
  }
  .ho-system__sep{ color:var(--border-strong); }
  .ho-system__clock{
    font-variant-numeric:tabular-nums;
    letter-spacing:0.1em;
    color:var(--text-muted);
  }
  @media (max-width:520px){
    .ho-system{ font-size:0.62rem; letter-spacing:0.1em; gap:0.5rem; }
  }

  /* ------------------------------------------------------------
     Motion safety for everything above.
     ------------------------------------------------------------ */
  @media (prefers-reduced-motion:reduce){
    .skyline.is-visible span{ animation:none !important; transform:scaleY(1) !important; }
    .ho-system__dot{ animation:none !important; opacity:0.8 !important; }
    .projects__grid .card::after{ transition:none !important; }
  }

  /* ============================================================
     v0.94.0 — Banner: "Survey".

     The landing banner becomes an architectural survey in progress.
     hero.js injects three layers into the existing .hero markup, so
     the WordPress page content is untouched and stays that way
     through future iterations:

       .hero-canvas   a wireframe city, drawing itself in, rotating
                      slowly, parallaxing with the pointer
       .hero-hud      corner registration marks, edge ticks, and four
                      readouts taken from the site's own content
       .hero-readout  the discipline line under the paragraph

     The headline now runs through the decode effect instead of the
     v0.91 wipe — the same treatment as "Selected work", which is the
     one that was asked for here.

     Still monochrome. The "cyber" read comes from structure, motion
     and instrument vernacular, never from colour.
     ============================================================ */

  /* ------------------------------------------------------------
     Layer stack inside the banner.
     ------------------------------------------------------------ */
  .hero{ overflow:hidden; }
  .hero-canvas{
    position:absolute;
    inset:0;
    width:100%;
    height:100%;
    z-index:0;
    pointer-events:none;
    /* Soften only the outer edge of the frame, so the drawing runs nearly
       full-bleed and reads as a space rather than a rectangle pasted onto
       the page. Legibility behind the headline is handled by the scrim
       below, NOT by masking the city out of the middle — masking the centre
       hid the structure exactly where it needed to be seen. */
    -webkit-mask-image:radial-gradient(ellipse 96% 92% at 50% 52%, #000 62%, transparent 99%);
            mask-image:radial-gradient(ellipse 96% 92% at 50% 52%, #000 62%, transparent 99%);
    opacity:0;
    animation:ho-canvas-in 1.6s var(--ease) 0.15s forwards;
  }
  @keyframes ho-canvas-in{ to{ opacity:1; } }

  /* A pool of near-black under the headline. The city stays at full strength
     around it while the type keeps its contrast — the alternative was dimming
     the whole drawing, which cost the structure far more than it gained. */
  .hero.has-survey .hero__inner::before{
    content:"";
    position:absolute;
    left:50%;
    top:50%;
    transform:translate(-50%,-50%);
    width:150%;
    height:190%;
    z-index:-1;
    pointer-events:none;
    background:radial-gradient(ellipse 52% 46% at 50% 44%,
                rgba(10,10,10,0.90) 0%,
                rgba(10,10,10,0.72) 42%,
                rgba(10,10,10,0) 76%);
  }

  /* The flat bar motif retires inside the banner: the survey IS the skyline,
     built properly in three dimensions. Two versions of the same motif in one
     frame read as clutter. The bars are untouched everywhere else — the
     /in-progress/ page still leads with them. */
  .hero.has-survey .skyline{ display:none; }
  .hero__inner{ z-index:3; }

  /* ------------------------------------------------------------
     HUD — registration marks and edge ticks.
     ------------------------------------------------------------ */
  .hero-hud{
    position:absolute;
    inset:clamp(18px, 3.4vw, 40px);
    z-index:2;
    pointer-events:none;
    opacity:0;
    animation:ho-hud-in 1.1s var(--ease) 0.75s forwards;
  }
  @keyframes ho-hud-in{ to{ opacity:1; } }

  .hero-hud__mark{
    position:absolute;
    width:22px;
    height:22px;
    --c:rgba(242,242,237,0.30);
  }
  .hero-hud__mark--tl{ top:0;    left:0;
    border-top:1px solid var(--c); border-left:1px solid var(--c); }
  .hero-hud__mark--tr{ top:0;    right:0;
    border-top:1px solid var(--c); border-right:1px solid var(--c); }
  .hero-hud__mark--bl{ bottom:0; left:0;
    border-bottom:1px solid var(--c); border-left:1px solid var(--c); }
  .hero-hud__mark--br{ bottom:0; right:0;
    border-bottom:1px solid var(--c); border-right:1px solid var(--c); }

  /* A measured rule down each side — the survey's scale bar. */
  .hero-hud__ticks{
    position:absolute;
    top:52px; bottom:52px;
    left:0; right:0;
  }
  .hero-hud__ticks::before,
  .hero-hud__ticks::after{
    content:"";
    position:absolute;
    top:0; bottom:0;
    width:5px;
    background-image:linear-gradient(to bottom,
      rgba(242,242,237,0.26) 0 1px, transparent 1px 100%);
    background-size:100% 17px;
  }
  .hero-hud__ticks::before{ left:0; }
  .hero-hud__ticks::after { right:0; }

  .hero-hud__label{
    position:absolute;
    font-family:var(--font-mono);
    font-size:0.63rem;
    letter-spacing:0.18em;
    text-transform:uppercase;
    color:var(--text-faint);
    white-space:nowrap;
  }
  .hero-hud__label--tl{ top:-2px;    left:32px;  }
  .hero-hud__label--tr{ top:-2px;    right:32px; }
  .hero-hud__label--bl{ bottom:-2px; left:32px;  }
  .hero-hud__label--br{ bottom:-2px; right:32px; }
  .hero-hud__clock{
    font-variant-numeric:tabular-nums;
    color:var(--text-muted);
    letter-spacing:0.12em;
  }

  /* Phones. Three corrections the desktop layout doesn't need:
     the whole HUD drops below the fixed header, because at this width its
     top readouts ran straight into the logo and the menu button; the side
     scale rules go, since they crowd a headline that now fills the width;
     and the lower two readouts go with them. */
  @media (max-width:680px){
    /* Compressed intro. hero.js speeds the drawing itself up; these bring the
       layer fades in behind it so the banner settles in well under a second
       instead of still assembling as the visitor scrolls away. */
    .hero-canvas { animation-duration:0.55s; animation-delay:0.04s; }
    .hero-hud    { animation-duration:0.40s; animation-delay:0.26s; }
    .hero-readout{ animation-duration:0.40s; animation-delay:0.36s; }

    .hero-hud{ inset:86px 18px 20px; }
    .hero-hud__ticks{ display:none; }
    .hero-hud__label--bl,
    .hero-hud__label--br{ display:none; }
    .hero-hud__label{ font-size:0.56rem; letter-spacing:0.14em; }
    .hero-hud__label--tl{ left:28px; }
    .hero-hud__label--tr{ right:28px; }
    .hero-hud__mark{ width:16px; height:16px; }
  }

  /* ------------------------------------------------------------
     Readout strip under the paragraph.
     ------------------------------------------------------------ */
  .hero-readout{
    display:inline-flex;
    align-items:center;
    gap:0.6rem;
    margin:0 auto var(--space-4);
    padding:0.45rem 0.95rem;
    border:1px solid var(--border);
    border-radius:2px;
    background:rgba(19,19,17,0.55);
    backdrop-filter:blur(6px);
    font-family:var(--font-mono);
    font-size:0.68rem;
    letter-spacing:0.16em;
    text-transform:uppercase;
    color:var(--text-muted);
    opacity:0;
    animation:ho-hud-in 1s var(--ease) 1s forwards;
  }
  .hero-readout__dot{
    width:5px; height:5px;
    border-radius:50%;
    background:var(--text-muted);
    flex-shrink:0;
    animation:ho-pulse 2.6s ease-in-out infinite;
  }
  @media (max-width:680px){
    .hero-readout{
      font-size:0.55rem;
      letter-spacing:0.08em;
      padding:0.4rem 0.7rem;
      max-width:100%;
      text-align:center;
    }
    /* The dot is vertically centred against its flex row. If the line wraps
       it ends up floating beside the gap between two lines, reading as a
       stray mark rather than a status light — so at this width the text
       carries the strip on its own. */
    .hero-readout__dot{ display:none; }
    /* The banner now has a drawn floor filling the lower frame, so it no
       longer needs the empty run of padding that used to give it presence. */
    .hero{ padding-bottom:calc(var(--space-6) * 0.85); }
  }

  /* ------------------------------------------------------------
     Headline: the decode replaces the v0.91 wipe.
     The wipe's clip-path would have chopped the scrambling glyphs
     mid-run, so only the paragraph and the buttons still wipe.
     min-height holds the line while characters swap, which is what
     stops the banner shifting as it resolves.
     ------------------------------------------------------------ */
  .hero h1{
    animation:none;
    opacity:1;
    clip-path:none;
    font-variant-numeric:tabular-nums;
  }
  .hero h1.is-decoding{ opacity:0.95; }

  /* ------------------------------------------------------------
     Banner proportions. The survey needs room to read as a space,
     so the content sits lower in a taller frame.
     ------------------------------------------------------------ */
  @media (min-width:861px){
    .hero{ min-height:100vh; padding-top:5rem; }
    .hero h1{ font-size:clamp(2.6rem, 7.8vw, 6rem); }
  }

  @media (prefers-reduced-motion:reduce){
    .hero-canvas,
    .hero-hud,
    .hero-readout{ animation:none !important; opacity:1 !important; }
    .hero-readout__dot{ animation:none !important; }
  }

  /* ============================================================
     v0.94.1 — Banner intro, compressed on phones.

     Desktop timing is unchanged. On a phone the banner fills a
     screen the visitor starts scrolling within a second or two, so
     a sequence paced for a desktop viewing distance finished after
     the banner had already left the screen — the effect existed but
     was never seen. The whole intro now runs on a roughly 3x faster
     clock at 680px and below, keeping its order (ground, near
     towers, far towers, HUD) but landing in well under a second.

     Split across three files, all gated on the same 680px:
       hero.js        SPEED divides every drawing timing
       instrument.js  headline decode: fewer, shorter frames
       style.css      the layer fade-ins, just above
     ============================================================ */

  /* ============================================================
     v0.96.0 — Ten-item menu + Services page.
     (v0.95.0 is skipped on purpose: that number belongs to the
     shelved Media build, and reusing it would make the rollback
     chain ambiguous.)

     1. MENU. About · Services · Projects · Media · Partners ·
        Membership · Tickets · Talent · Shop · Contact. Shop points at
        /in-progress/ for now (see ho_nav_links()).

        Ten labels need ~490px of text at 0.85rem. Measured in a real
        browser: with the old 36px gaps the row no longer fit inside
        the 1120px column at ANY width — the menu overran the logo
        and, on wide screens, the right edge of the column. So:

          ≥1024px  one row, gaps rescaled to clamp(16px,1.75vw,26px).
                   Logo locked at full size (it was being squeezed).
          681–1023 the hamburger + dropdown that phones already use.
                   This band is tablets; the row would need type too
                   small to read. Same markup, same nav.js — only the
                   breakpoint the dropdown CSS applies at is extended.
          ≤680     unchanged.

        The v0.92 681–820px tightening is overridden inside the
        dropdown so tablet menu items read at normal size.
     ------------------------------------------------------------ */
  .nav .mark{ flex-shrink:0; }
  .nav__links a{ white-space:nowrap; }

  @media (min-width:1024px){
    .nav__links{ gap:clamp(1rem, 1.75vw, 1.625rem); }
  }

  @media (min-width:681px) and (max-width:1023px){
    .nav__links{
      position:fixed;
      top:60px;
      left:0;
      right:0;
      flex-direction:column;
      gap:1.1rem;
      background:var(--bg);
      border-bottom:1px solid var(--border);
      padding:var(--space-3) clamp(1.5rem, 6vw, 2.5rem) var(--space-4);
      transform:translateY(-115%);
      visibility:hidden;
      transition:transform 0.35s var(--ease), visibility 0s linear 0.35s;
    }
    .nav__links.is-open{
      transform:translateY(0);
      visibility:visible;
      transition:transform 0.35s var(--ease);
    }
    .nav__links a{ font-size:0.85rem; }
    .nav__toggle{ display:flex; }
    .admin-bar .nav__links{ top:92px; }
  }
  @media (min-width:681px) and (max-width:782px){
    .admin-bar .nav__links{ top:106px; }
  }

  /* Ten rows can outgrow a short screen (a phone held sideways):
     let the dropdown scroll rather than run off the bottom. */
  @media (max-width:1023px){
    .nav__links{
      max-height:calc(100vh - 60px);
      overflow-y:auto;
      overscroll-behavior:contain;
    }
  }

  /* ------------------------------------------------------------
     2. SERVICES PAGE (/services/, rendered by page-services.php).

     Reuses the Projects page head (.projects-page / __head), so the
     two pages share their top spacing and the "Services" title runs
     through the same decode effect. Below it:

       .svc-index   a sticky numbered index (desktop only)
       .svc         one discipline: counter, title, its lines
       .svc-cta     the way out, to the home contact form

     Every list/heading margin is set explicitly here, because these
     elements sit outside .entry-content and would otherwise pick up
     Astra's own ul/h3 defaults. Monochrome only. The bullet marker
     is a short vertical bar — the skyline motif at text scale — and
     the bars of a discipline rise when it is hovered.
     ------------------------------------------------------------ */
  .services-page{ padding-bottom:var(--space-7); }
  .services-page__head{ margin-bottom:var(--space-5); }

  .svc-layout{
    display:grid;
    grid-template-columns:minmax(0, 1fr);
    gap:var(--space-5);
  }
  @media (min-width:960px){
    .svc-layout{
      grid-template-columns:260px minmax(0, 1fr);
      gap:var(--space-6);
      align-items:start;
    }
  }

  /* The sticky index needs a page that scrolls on the viewport. Earlier
     blocks set overflow-x:hidden on both body and html, which turns body
     into its own (non-scrolling) scroll box and silently disables
     position:sticky. On this page only, body clips instead of hiding:
     same "no sideways scroll" result, but not a scroll box. Browsers
     without overflow:clip keep the hidden value above and the index
     simply sits still at the top — no breakage either way. */
  body.ho-services{ overflow-x:hidden; overflow-x:clip; overflow-y:visible; }

  /* Index */
  .svc-index{ display:none; }
  @media (min-width:960px){
    .svc-index{
      display:block;
      position:sticky;
      top:96px;
    }
  }
  .svc-index ol{
    list-style:none;
    margin:0;
    padding:0;
    border-top:1px solid var(--border);
  }
  .svc-index li{ margin:0; padding:0; }
  .svc-index a{
    display:flex;
    gap:0.9rem;
    align-items:baseline;
    padding:0.8rem 0;
    border-bottom:1px solid var(--border);
    font-size:0.82rem;
    line-height:1.4;
    color:var(--text-muted);
    transition:color 0.25s var(--ease);
  }
  .svc-index a:hover{ color:var(--text); }
  .svc-index__num{
    font-family:var(--font-mono);
    font-size:0.68rem;
    letter-spacing:0.1em;
    color:var(--text-faint);
    flex-shrink:0;
    font-variant-numeric:tabular-nums;
  }

  /* Disciplines */
  .svc{
    position:relative;
    padding:var(--space-4) 0 var(--space-5);
    border-top:1px solid var(--border);
    scroll-margin-top:84px;
  }
  .svc__num{
    margin:0 0 var(--space-2);
    font-family:var(--font-mono);
    font-size:0.72rem;
    letter-spacing:0.16em;
    color:var(--text-faint);
    font-variant-numeric:tabular-nums;
  }
  .svc__num span{ color:var(--border-strong); margin:0 0.2em; }
  .svc__title{
    margin:0 0 var(--space-3);
    max-width:24ch;
    font-family:var(--font-display);
    font-weight:600;
    font-size:clamp(1.45rem, 2.6vw, 2rem);
    line-height:1.12;
    letter-spacing:-0.01em;
    color:var(--text);
    text-wrap:balance;
  }
  .svc__items{
    list-style:none;
    margin:0;
    padding:0;
    max-width:640px;
  }
  .svc__items li{
    position:relative;
    margin:0;
    padding:0.7rem 0 0.7rem 1.4rem;
    border-bottom:1px solid var(--border);
    font-size:1rem;
    line-height:1.55;
    color:var(--text-muted);
  }
  .svc__items li:last-child{ border-bottom:none; }
  .svc__items li::before{
    content:"";
    position:absolute;
    left:0.15rem;
    top:calc(0.7rem + 0.32em);
    width:2px;
    height:0.9em;
    background:var(--text-faint);
    transform-origin:bottom;
    transition:transform 0.45s var(--ease), background-color 0.45s var(--ease);
  }
  .svc:hover .svc__items li::before{
    transform:scaleY(1.35);
    background:var(--text-muted);
  }
  .svc:hover .svc__items li:nth-child(2)::before{ transition-delay:60ms; }
  .svc:hover .svc__items li:nth-child(3)::before{ transition-delay:120ms; }
  .svc:hover .svc__items li:nth-child(4)::before{ transition-delay:180ms; }

  /* Way out */
  .svc-cta{
    display:flex;
    align-items:center;
    justify-content:space-between;
    gap:var(--space-3);
    flex-wrap:wrap;
    padding-top:var(--space-4);
    border-top:1px solid var(--border);
  }
  .svc-cta .eyebrow{ margin:0; }

  @media (max-width:680px){
    .services-page__head{ margin-bottom:var(--space-3); }
    .svc{ padding:var(--space-3) 0 var(--space-4); }
    .svc__items li{ font-size:0.95rem; }
  }

  @media (prefers-reduced-motion:reduce){
    .svc__items li::before{ transition:none !important; }
  }

  /* ============================================================
     v1.0.0 — "Broadcast". (19 Oct 2026)

     The site takes on the look of the static homepage
     (house-ozaki.html), at the owner's request. The homepage itself
     is no longer WordPress content: it is a Static Page served by
     the new House Ozaki Broadcast plugin, which also holds the TV
     Wall, the Instagram feed and the social channels.

     NOTHING ABOVE THIS NOTE WAS EDITED. The new look lives in its
     own file, assets/css/broadcast.css, loaded after this one. It
     re-points the tokens this file already uses (--bg, --text,
     --font-display…) and then re-skins each component. To go back
     to the v0.96.0 look, stop enqueueing it (functions.php).

     Also in v1.0.0:
       - header + footer: inc/broadcast.php (nine channels, shared
         with the homepage's drum menu via the plugin)
       - Projects: "Selected work" on top, "Project Archive" below
       - new templates: page-contact.php, page-tickets.php
       - fonts: Big Shoulders Display, IBM Plex Sans, VT323, self-
         hosted in assets/fonts (fonts-broadcast.css)
       - the black-and-white-only rule is retired; amber is the
         accent and on-air red marks a live state only
     ============================================================ */

  /* ============================================================
     v1.1.0 — Header + footer on static pages. (19 Oct 2026)

     The homepage (a static page) now sits between the same header
     and footer as every WordPress page. The theme supplies them to
     the Broadcast plugin through the hob_chrome filter
     (inc/broadcast.php); their styles are assets/css/chrome.css and
     their script assets/js/chrome.js. Both are self-contained (hoc-
     classes only) because a static page's own CSS is unknown in
     advance. Nothing in this file or broadcast.css changed.
     ============================================================ */

  /* ============================================================
     v1.1.1 — Projects page opens at the top. (19 Oct 2026)
     projects-browser.js: selecting the first project on load used
     scrollIntoView(), which scrolled the whole page down to the
     archive. It now scrolls only inside the project list. CSS
     unchanged.
     ============================================================ */

  /* ============================================================
     v1.1.2 — Services "Let's talk" goes to Contact. (18 Oct 2026)
     The button still pointed at the old one-page homepage's
     #contact section, so it landed on the homepage. It now uses
     ho_bc_channel_url('contact') (inc/broadcast.php), the same
     address as Contact in the menu. CSS unchanged.
     ============================================================ */
