:root{
  --ground: #FAEFE9;
  --sage:   #55604B;
  /* Her own Paper tokens, for the mobile chrome below. */
  --ink:    #3A3236;
  --muted:  #6B5F63;
  --rule:   #A9999B;
  --subtle: #8A7D80;
}

*{ box-sizing: border-box; }

html{ scroll-behavior: smooth; }

body{
  margin: 0;
  background: var(--ground);
}

/* A board and its hotspots share one positioning context, so every link is
   placed as a percentage of the artboard and holds at any window width. */
.sheet{
  position: relative;
  width: 100%;
  margin: 0 auto;
  line-height: 0;
  /* Titles are sized in cqw so they track the board image exactly. */
  container-type: inline-size;
}

.board{
  display: block;
  width: 100%;
  height: auto;
  opacity: 0;
  transition: opacity .5s ease;
}
.board.is-loaded{ opacity: 1; }

.hot{
  position: absolute;
  display: block;
  z-index: 2;
  border-radius: 2px;
  /* The drawn element is the affordance; this only confirms it on approach. */
  box-shadow: inset 0 -1px 0 0 transparent;
  background: transparent;
  transition: background .18s ease, box-shadow .18s ease;
}
/* --- a photograph that lifts when you are on it --------------------------
   The board is one flat JPEG, so a region of it cannot be animated. But the
   crops cut for the phone are pixel-identical to what the board shows and sit
   at exactly the same rectangles -- so laying one over its own region and
   fading it in is invisible, and the scale that follows reads as the
   photograph itself rising off the page. */
.liftable{
  overflow: visible;
}
.liftframe{
  position: absolute;
  overflow: hidden;      /* the frame never moves, so nothing shifts */
}
.lifted-img{
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transform: scale(1);
  transform-origin: center;
  transition: opacity .01s linear, transform 1.1s cubic-bezier(.22,.61,.36,1);
  will-change: transform;
}
.liftable:hover,
.liftable:focus-visible{ z-index: 4; }
.liftable:hover .lifted-img,
.liftable:focus-visible .lifted-img{
  opacity: 1;
  transform: scale(1.08);
}
@media (prefers-reduced-motion: reduce){
  .lifted-img{ transition: none; }
  .liftable:hover .lifted-img,
  .liftable:focus-visible .lifted-img{ transform: none; }
}

/* A credit over the image it belongs to. Hidden until the reader is on it, so
   the cover stays her photographs rather than a captioned grid. */
.cap{
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: .45em;
  padding: 10px;
  background: rgba(255, 255, 255, .72);
  color: var(--ink);
  font: 400 clamp(11px, 0.92vw, 15px)/1.35 "Cormorant Garamond", "Hoefler Text", "Iowan Old Style", Georgia, serif;
  letter-spacing: .06em;
  text-align: center;
  opacity: 0;
  transition: opacity .22s ease;
  pointer-events: none;
}
.cap b{
  display: block;
  font-weight: 400;
}
/* The credit line only reads as a credit when something follows it; a caption
   of one line is the title, not a byline. */
.cap b:first-child:not(:last-child){
  letter-spacing: .12em;
  text-transform: uppercase;
  font-size: .82em;
  color: var(--muted);
}

.hot:hover .cap,
.hot:focus-visible .cap,
.lshoots a:hover .cap,
.lshoots a:focus-visible .cap{ opacity: 1; }

/* The link is the positioning context for its own caption. */
.lshoots a{ position: relative; }

.hot:hover{
  background: rgba(85, 96, 75, .07);
  box-shadow: inset 0 -1px 0 0 var(--sage);
}
/* A hotspot that reveals its own image or credit needs no tint behind it. The
   hotspot is the grid cell, which can run well past the bottom of the
   photograph, so the tint and its underline drew a box around empty space. */
.hot.self-cued:hover{
  background: transparent;
  box-shadow: none;
}
.hot:focus-visible{
  outline: 2px solid var(--sage);
  outline-offset: 2px;
  background: rgba(85, 96, 75, .07);
}

/* Her display face, the same OTF the boards are set in. */
@font-face{
  font-family: "ThePaper Doll";
  src: url("../fonts/ThePaperDoll.otf") format("opentype");
  font-weight: 400;
  font-style: normal;
  /* `block`, not `swap`: the fallback is a different script face at a
     different width, so swapping mid-load makes the title visibly change shape
     and re-wrap. Better to show nothing for the moment it takes -- and the
     font is preloaded, so that moment is short. */
  font-display: block;
}

/* These boards are exported WITHOUT their titles, so the title can be
   written on arrival instead of already sitting there finished. */
.title{
  position: absolute;
  font-family: "ThePaper Doll", "Snell Roundhand", cursive;
  color: #3A3236;
  white-space: pre;
  -webkit-font-smoothing: antialiased;
}
/* The clip lives on an inner element, never on the observed one: an element
   clipped to zero area never fires IntersectionObserver at all. */
.title > i{ font-style: normal; display: inline-block; }
/* Added by script, so a title is never hidden when JS does not run. */
/* Hidden until the pen reaches it; JS animates clip-path letter by letter. */
.title.will-write > i{ clip-path: polygon(-300px -100%, -300px -100%, -300px 200%, -300px 200%); }

/* The scrim is retired. It covered the nav painted into each board and lifted
   on hover, which meant the painted bar and the HTML bar could both be seen,
   in different places, with different labels -- two bars. The painted one now
   simply stands at the top of its board, and the bar below is the same nav,
   set to match it, arriving only once that one has scrolled away. */
.navband{
  display: none;              /* wide: the painted nav IS the at-rest nav */
  position: absolute;
  left: 0;
  right: 0;
  z-index: 1;
}
.navband::before{
  content: "";
  position: absolute;
  inset: 0;
  background-image: var(--scrim);
  background-size: 100% 100%;
  opacity: 1;
  transition: opacity .4s ease;
}
/* The scrim never clears. It used to lift on hover, which brought the painted
   nav back while the HTML bar was elsewhere on the page -- two different bars,
   with different labels, depending on where the cursor was. The bar below is
   the only nav now, and the board's own masthead stays covered. */

@media (prefers-reduced-motion: reduce){
  html{ scroll-behavior: auto; }
  *{ transition-duration: .01ms !important; }
  .title.will-write > i{ clip-path: none !important; }
  .navband::before{ opacity: 1; }
}


/* ==========================================================================
   Narrow screens only.

   The boards are authored at 1440px. A phone lays them out at ~390, so the
   whole page renders at 27%: baked nav type lands at 3.5px, footer links at
   3px, and their hotspots at 8px and 6px tall against a 44px minimum. The
   artwork survives that reduction -- the titles, the flats, the shoot grid all
   still read -- but nothing on it can be operated.

   So below the breakpoint the untappable hotspots step aside (build.py marks
   them `m-off` by measuring what they would render at) and real HTML chrome
   carries the same destinations at a readable size. A zoom control reaches the
   fine print the image still holds. Above the breakpoint none of this exists
   and the desktop page is exactly what it was.
   ========================================================================== */

/* The footer chrome is for narrow screens only; the bar serves every screen. */
.mfoot{ display: none; }

/* No box at all above the breakpoint -- the board sits directly in <main>. */
.crop{ display: contents; }
/* The split cover is narrow-screen only; desktop keeps the single board. */
.split{ display: none; }

/* ==========================================================================
   The nav bar -- one component, every screen.

   Each board paints its own masthead and it scrolls away with the page, so
   past the first screen there is no way between sections without going back
   up. This brings the same nav back, and only once the reader has left the
   painted one behind -- which is also what lets the cover show her nameplate
   once, as artwork, before any chrome arrives.

   Sizes are clamped rather than branched, so a phone and a desktop render the
   same design at whatever size their width allows, instead of two separate
   treatments that drift apart.
   ========================================================================== */
.mnav{
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 6;
  /* Her own masthead band, the same value it carries in Paper: ivory at 78%,
     so the page shows through exactly as it does behind the painted one. */
  background: rgba(252, 248, 240, .78);
  border-bottom: 1px solid var(--rule);
  padding: clamp(6px, 0.7vw, 14px) 0 0;
  text-align: center;
  transform: translateY(-101%);
  transition: transform .3s ease;
}
body.nav-shown .mnav{ transform: translateY(0); }

/* The wordmark belongs to the phone's bar; a wide screen shows the nav row
   alone, because that is what her boards paint. */
.mwm{ display: none; }

/* One row, never two: a nav that reflows pushes the board down the page.
   Centred with auto margins rather than `justify-content`, which clips the
   first item unreachably when the content overflows its scroll container. */
.mrow{
  display: flex;
  gap: clamp(6px, 2.05vw, 30px);
  overflow-x: auto;
  scrollbar-width: none;
  padding: 0 4px 4px;
}
.mrow::-webkit-scrollbar{ display: none; }
.mrow a:first-child{ margin-left: auto; }
.mrow a:last-child{ margin-right: auto; }
.mrow a{
  flex: 0 0 auto;
  font: 400 clamp(9px, 0.95vw, 13px)/1 "Cormorant Garamond", "Hoefler Text", "Iowan Old Style", Georgia, serif;
  /* Tracking in px so it scales with the type instead of branching: ~0.1em on
     a phone, ~0.16em on a desktop, which is what her painted nav is set at. */
  letter-spacing: clamp(0.9px, 0.14vw, 2.1px);
  text-transform: uppercase;
  color: var(--ink);
  text-decoration: none;
  /* Small type, a 44px target: the padding is the tap area. */
  padding: 18px 2px 17px;
  transition: color .18s ease;
}
.mrow a:hover{ color: var(--ink); }
.mrow a[aria-current]{ color: var(--sage); }

@media (prefers-reduced-motion: reduce){
  .mnav{ transition: none; }
}

@media (max-width: 760px), (max-width: 1100px) and (pointer: coarse){

  /* --- the board itself ------------------------------------------------- */

  /* Zoomed, the sheet goes to the board's own authored width and `main` pans.
     1440 is the meaningful stop, not an arbitrary multiple: it renders the
     board at 1:1, which is the only scale at which the type baked into it is
     the size it was drawn. Every overlay is positioned in % and sized in cqw,
     so hotspots and titles come along for free -- that is the whole reason
     this architecture takes a zoom at all. */
  /* Only while zoomed. At rest `overflow-x` must stay visible: it would
     otherwise clip any title overlay that runs past the board's right edge,
     which is one of the two ways a title can appear cut off on a phone. */
  body.is-zoomed main{
    overflow-x: auto;
    overscroll-behavior-x: contain;
  }
  /* Widening the crop widens the sheet inside it, and `aspect-ratio` keeps the
     footer cut at exactly the same board row in both states -- at width 1440
     the height resolves to --crop-h px on the nose. */
  body.is-zoomed .crop{ width: 1440px; }

  /* --- the split cover ------------------------------------------------- */

  /* Her About band sits mid-board, so it cannot be cropped off an edge the way
     the nav and footer were, and real copy at a readable size needs far more
     room than the band occupies at phone scale. So the board becomes two
     windows onto itself with a real About section between them. */
  .crop[data-split]{ display: none; }

  .split{
    display: block;
    /* cqw for the title overlay resolves here, at the same width the sheet
       would have had, so its type scales exactly as before. */
    container-type: inline-size;
  }
  body.is-zoomed .split{ width: 1440px; }

  .slice{
    position: relative;
    background-image: var(--board);
    background-repeat: no-repeat;
    /* One image, shown twice: the same file the desktop <img> already pulls,
       so the second window costs no extra download. */
    background-size: 100% auto;
    background-position: 0 calc(100cqw * var(--y) / -1440);
    aspect-ratio: 1440 / var(--h);
  }

  /* --- lifted text ------------------------------------------------------ */
  /* Type sizes are chosen for the phone, not scaled from the board: the board's
     15px would arrive at 4px. Faces, colours and wording are hers. */

  .lifted{ padding: 34px 22px 38px; }

  /* Page titles ride the board, so they are sized in cqw and scale with it.
     A phone wants them larger than that ratio gives. */
  .title{
    --tscale: 1.18;
    /* Never past the right edge, whatever the scale. */
    max-width: calc(100% - var(--tleft, 0%));
  }
  .title.nameplate{ --tscale: 1.38; --tlead: 1.14; }
  /* Fashion Drawing's title is set smaller on the board than the others
     (5.97cqw against 6.81), so the shared scale left it trailing its own
     body copy. It leads its page now. */
  .split-drawing .title{ --tscale: 1.72; --tlead: 1.14; }
  /* The title is positioned on its board window, so scaling it up pushes it
     past the bottom of that window and onto whatever follows. The block below
     makes room rather than the title shrinking back. */
  .split-drawing .lcopyblock{ padding-top: 148px; }
  .split-drawing .lcopy{ font-size: 15px; }
  .split-drawing .dropcap{ font-size: 33px; margin-top: 2px; }

  /* "Merchandising" is one long word in a script face -- it cannot wrap, so it
     is capped against the viewport as well as set in px. At 48px it ran 393px
     wide against 346px of measure and pushed a scrollbar onto the page. */
  .ltitle{
    margin: 0 0 22px;
    font: 400 min(48px, 10.5vw)/1.2 "ThePaper Doll", "Snell Roundhand", cursive;
    color: var(--ink);
  }
  .lcopy{
    margin: 0 0 17px;
    font: 400 16px/1.65 "Cormorant Garamond", "Hoefler Text", "Iowan Old Style", Georgia, serif;
    color: #4A3C30;
  }
  .lcopy:last-of-type{ margin-bottom: 0; }

  /* Her bordered business panel, stacked: label over value reads far better at
     this width than the board's two-column lanes. */
  .lrecord{
    margin-top: 30px;
    border: 1px solid var(--ink);
    padding: 22px 20px 6px;
  }
  .lrlabel{
    margin: 0 0 16px;
    font: italic 300 13px/1 "Cormorant Garamond", "Hoefler Text", "Iowan Old Style", Georgia, serif;
    letter-spacing: .16em;
    text-transform: uppercase;
    color: var(--sage);
  }
  .lrecord dl{ margin: 0; }
  .lrecord dt{
    font: 500 10.5px/1 "Cormorant Garamond", "Hoefler Text", "Iowan Old Style", Georgia, serif;
    letter-spacing: .16em;
    text-transform: uppercase;
    color: var(--subtle);
  }
  .lrecord dd{
    margin: 5px 0 16px;
    font: 400 15px/1.45 "Cormorant Garamond", "Hoefler Text", "Iowan Old Style", Georgia, serif;
    color: var(--ink);
  }

  /* ThePaper Doll's loops overshoot its em box: at 22px its ink measures 31px,
     so anything under ~1.45 leading makes wrapped headings collide. */

  /* Her drop cap, at a size that still reads as one. */
  .dropcap{
    float: left;
    margin: 6px 9px 0 0;
    font: 400 46px/0.72 "ThePaper Doll", "Snell Roundhand", cursive;
    color: var(--sage);
  }
  .lcopyblock .lcopy::after{ content: ""; display: block; clear: both; }

  /* Technical flats: two across instead of four. */
  .lcardhead{
    margin: 0 0 20px;
    font: 400 22px/1.55 "ThePaper Doll", "Snell Roundhand", cursive;
    color: var(--ink);
  }
  .lgrid{
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 22px 14px;
  }
  .lgrid img{
    display: block;
    width: 100%;
    height: auto;
  }
  .lgrid p{
    margin: 9px 0 0;
    font: 400 13px/1.45 "Cormorant Garamond", "Hoefler Text", "Iowan Old Style", Georgia, serif;
    color: var(--muted);
  }

  /* A section heading, with the swelled rule the board draws beside it. */
  .lhead{ padding-bottom: 20px; }
  .lhead h2{
    display: flex;
    align-items: center;
    gap: 16px;
    margin: 0;
    font: 400 22px/1.55 "ThePaper Doll", "Snell Roundhand", cursive;
    color: var(--ink);
  }
  .lhead h2::after{
    content: "";
    flex: 1;
    height: 1px;
    background: var(--ink);
    opacity: .45;
  }

  /* One shoot per row, at the page's full measure. */
  .lshoots{ padding-top: 8px; }
  .lshoots a{
    display: block;
    margin-bottom: 18px;
  }
  .lshoots a:last-child{ margin-bottom: 0; }
  .lshoots img{
    display: block;
    width: 100%;
    height: auto;
  }

  /* Palette strips: her six sampled colours, with the codes readable. */
  .lpal ul{
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    gap: 5px;
  }
  .lpal i{
    display: block;
    height: 44px;
  }
  .lpal span{
    display: block;
    margin-top: 6px;
    font: 400 9px/1 "Cormorant Garamond", "Hoefler Text", "Iowan Old Style", Georgia, serif;
    letter-spacing: .06em;
    text-align: center;
    color: var(--muted);
    font-variant-numeric: lining-nums;
  }

  /* A mood board whose title sits beside it on the board; stacked here. */
  .lboard h2{
    margin: 0;
    font: 400 22px/1.55 "ThePaper Doll", "Snell Roundhand", cursive;
    color: var(--ink);
  }
  .lcredit{
    margin: 12px 0 0;
    font: 400 10px/1 "Cormorant Garamond", "Hoefler Text", "Iowan Old Style", Georgia, serif;
    letter-spacing: .22em;
    text-transform: uppercase;
    color: var(--subtle);
  }
  .lboard img{
    display: block;
    width: 100%;
    height: auto;
    margin-top: 22px;
  }

  /* The band, rebuilt as text. Colours and the lace-framed portrait are hers,
     lifted straight off the board. */
  .about{
    background: var(--band);
    padding: 36px 22px 42px;
  }
  .aportrait{
    width: 210px;
    aspect-ratio: 480 / 600;
    margin: 0 auto 28px;
    background-size: cover;
    background-position: center;
  }
  .alabel{
    margin: 0 0 15px;
    font: italic 300 12px/1 "Cormorant Garamond", "Hoefler Text", "Iowan Old Style", Georgia, serif;
    letter-spacing: .16em;
    text-transform: uppercase;
    color: var(--sage);
  }
  .acopy{
    margin: 0;
    font: 400 15px/1.7 "Cormorant Garamond", "Hoefler Text", "Iowan Old Style", Georgia, serif;
    color: #000;
  }

  /* Cut the baked footer off the bottom. --crop-h is the board row of the rule
     above her wordmark, measured off each export rather than guessed. */
  .crop{
    display: block;
    overflow: hidden;
    aspect-ratio: 1440 / var(--crop-h);
  }

  /* Placed for a mouse, unreachable for a thumb. Their destinations are all
     repeated in the chrome above and below. */
  .hot.m-off{ display: none; }

  /* No hover on a touch screen, so what is left gets a resting hairline
     instead -- otherwise a tappable photograph looks identical to a flat one. */
  .hot{ box-shadow: inset 0 -1px 0 0 rgba(85, 96, 75, .35); }

  /* --- chrome --------------------------------------------------------- */

  .mfoot{ display: block; }

  /* The nav painted into each board is about 3px of type at this width, and
     the bar below is present from the top -- so the painted one is not a
     second option, it is illegible clutter directly under a legible bar. The
     scrim covers it with a patch lifted from that board's own background. */
  .navband{ display: block; }

  /* The phone's bar is the one she had before: present from the top rather
     than arriving on scroll, and carrying her wordmark. The board's painted
     masthead is unreadable at this width, so there is nothing for it to
     duplicate and every reason for it to be there the whole time. */
  .mnav{
    position: sticky;
    transform: none;
    background: var(--ground);
    padding: 4px 0 0;
  }
  /* ThePaper Doll's loops overshoot its em box badly -- at line-height 1 the
     ascenders are cut off by the top of the screen. */
  .mwm{
    display: block;
    font-family: "ThePaper Doll", "Snell Roundhand", cursive;
    font-size: 21px;
    line-height: 1.5;
    color: var(--ink);
    text-decoration: none;
    padding: 5px 16px 1px;
  }
  /* One nameplate per page: the cover already has hers, large, just below. */
  .mnav-cover .mwm{ display: none; }

  .mrow{
    gap: 9px;
    padding: 0 6px;
  }
  .mrow a{
    font-size: clamp(9px, 2.45vw, 11px);
    letter-spacing: .11em;
    color: var(--muted);
    padding: 18px 1px 17px;
  }

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

  .mfoot{
    /* The trailing space is for the zoom control, which is fixed to the
       bottom right and would otherwise sit on the last line. */
    padding: 34px 22px 86px;
    border-top: 1px solid var(--rule);
  }
  .mstep,
  .mlinks a{
    display: block;
    font: 400 15px/1.3 "Cormorant Garamond", "Hoefler Text", "Iowan Old Style", Georgia, serif;
    color: var(--ink);
    text-decoration: none;
    padding: 13px 0;
  }
  .mstep{
    color: var(--sage);
    letter-spacing: .04em;
    padding-bottom: 20px;
    border-bottom: 1px solid var(--rule);
    margin-bottom: 6px;
  }
  .mlinks{
    list-style: none;
    margin: 0;
    padding: 0;
  }
  .mlinks li + li{ border-top: 1px solid rgba(169, 153, 155, .4); }
  .mlinks a{ word-break: break-word; }
  .mfine{
    margin: 26px 0 0;
    font: 400 9.5px/1 "Cormorant Garamond", "Hoefler Text", "Iowan Old Style", Georgia, serif;
    letter-spacing: .26em;
    text-transform: uppercase;
    color: var(--rule);
  }

  /* --- zoom ------------------------------------------------------------- */

  /* The boards still carry real copy -- captions, spec tables, the credits
     rows -- that no amount of chrome can lift off a flat JPEG. Pinch works,
     but it fights the page scroll; this is the deliberate way in. */
  .zoom{
    position: fixed;
    right: 14px;
    bottom: 14px;
    z-index: 6;
    width: 46px;
    height: 46px;
    border: 1px solid var(--rule);
    border-radius: 50%;
    background: var(--ground);
    color: var(--ink);
    font: 400 15px/1 "Cormorant Garamond", "Hoefler Text", "Iowan Old Style", Georgia, serif;
    letter-spacing: .04em;
    box-shadow: 0 1px 6px rgba(58, 50, 54, .14);
    cursor: pointer;
  }
  .zoom[aria-pressed="true"]{
    background: var(--sage);
    border-color: var(--sage);
    color: var(--ground);
  }
}
