/* =========================================================================
   Prabhat — Portfolio & Shop
   Sketch / marker-on-graph-paper aesthetic (see Wireframe-Sketch)
   ========================================================================= */

:root {
  /* --- Palette --- */
  --bg: #f6f5f0;
  --bg-alt: #eef0ee;
  --grid: rgba(0, 0, 0, 0.06);
  --ink: #222222;
  --ink-soft: #555555;
  --paper: #ffffff;
  --paper-shadow: rgba(0, 0, 0, 0.12);

  --marker-blue: #2a5ab0;
  --marker-red: #d03030;
  --marker-orange: #f28c28;
  --marker-yellow: #fff3a0;
  --marker-pink: #ffc0cb;
  --marker-green: #4aa66b;

  --border: 2px solid #222222;
  --border-soft: 2px dashed #bbbbbb;
  --shadow-hard: 6px 6px 0px rgba(0, 0, 0, 0.15);
  --shadow-hard-lg: 10px 10px 0px rgba(0, 0, 0, 0.18);

  /* --- Type --- */
  /* Kalam sits in every stack after the Latin face. Browsers pick a font per
     character, and none of the Latin faces carry Devanagari — so Latin keeps
     its own hand and every Nepali glyph falls through to Kalam. One rule, no
     lang attributes needed. */
  --font-display: 'Caveat', 'Kalam', cursive;
  --font-hand: 'Architects Daughter', 'Kalam', cursive;
  --font-hand2: 'Patrick Hand', 'Kalam', cursive;
  --font-script: 'Caveat', 'Kalam', cursive;
  --font-deva: 'Kalam', 'Nirmala UI', 'Noto Sans Devanagari', 'Mangal', sans-serif;
  --font-body: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
               'Kalam', Arial, sans-serif;

  /* The lower bound matters more than the upper one: it is what a 360px
     phone actually gets, and 3.4rem there ran the name into both edges. */
  --fs-hero: clamp(2.5rem, 9vw, 7rem);
  --fs-h1: clamp(2.4rem, 5vw, 4rem);
  --fs-h2: clamp(1.9rem, 4vw, 3.4rem);
  --fs-h3: clamp(1.3rem, 1.8vw, 1.7rem);
  --fs-body: clamp(0.95rem, 1vw, 1.1rem);

  --space: clamp(1rem, 3vw, 2.5rem);
}
/* --- Reset --- */
* { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
/* The site is paper: one light look, on purpose. Saying so stops a phone in
   dark mode painting the parts the page does not control — the scrollbar,
   the open <select> list, autofill — in dark chrome against it. */
html { color-scheme: light; }
body {
  font-family: var(--font-body);
  font-size: var(--fs-body);
  color: var(--ink);
  background-color: var(--bg);
  background-image:
    linear-gradient(var(--grid) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid) 1px, transparent 1px);
  background-size: 24px 24px;
  line-height: 1.5;
}
img { max-width: 100%; display: block; }
a { color: var(--marker-blue); text-decoration: none; }
h1, h2, h3 { font-family: var(--font-display); line-height: 1.1; color: var(--ink); }
/* Anything tagged as Nepali / Hindi / Urdu uses Kalam outright. The per-glyph
   fallback in the stacks above handles untagged Devanagari, but it cannot help
   in --font-body: Segoe UI and Arial *do* carry Devanagari, so they would win
   before Kalam was ever reached. */
[lang="ne"], [lang="hi"], [lang="ur"],
[lang="ne"] *, [lang="hi"] *, [lang="ur"] * {
  font-family: var(--font-deva);
}
::selection { background: var(--marker-yellow); color: var(--ink); }
/* --- Layout helpers --- */
.container {
  width: min(1160px, 92vw);
  margin-inline: auto;
}
/* 7vw grew the padding with the window rather than with the content, so on
   a wide screen a section stood 961px tall against 832px of usable height
   and its pager fell below the fold. Capped, so a whole section lands on one
   screen when its link is tapped. */
section { padding-block: clamp(1.75rem, 3vw, 2.5rem); position: relative; }
.section-head {
  text-align: center;
  margin-bottom: clamp(1rem, 1.6vw, 1.4rem);
}
.section-head h2 {
  font-size: var(--fs-h2);
  display: inline-block;
  position: relative;
}
.section-head h2::after {
  content: '';
  position: absolute;
  left: -6px; right: -6px; bottom: 4px;
  height: 12px;
  background: var(--marker-yellow);
  z-index: -1;
  transform: skewX(-8deg);
  opacity: 0.85;
}
.section-head p {
  font-family: var(--font-hand2);
  color: var(--ink-soft);
  margin-top: 0.6rem;
  font-size: 1.15rem;
}
/* --- Hand-drawn wobbly button --- */
.btn {
  font-family: var(--font-hand2);
  font-size: 1.2rem;
  display: inline-block;
  padding: 0.6rem 1.4rem;
  border: var(--border);
  border-radius: 10px;
  background: var(--paper);
  color: var(--ink);
  cursor: pointer;
  box-shadow: var(--shadow-hard);
  transition: transform 0.15s ease, box-shadow 0.15s ease, background 0.15s ease;
  transform: rotate(-1deg);
  text-decoration: none;
  line-height: 1.2;
}
.btn:hover { transform: rotate(1deg) translateY(-2px); box-shadow: var(--shadow-hard-lg); }
.btn:active { transform: rotate(0deg) translateY(1px); box-shadow: 2px 2px 0px var(--paper-shadow); }
.btn.blue { background: var(--marker-blue); color: #fff; }
.btn.orange { background: var(--marker-orange); color: #fff; }
.btn.small { font-size: 1rem; padding: 0.4rem 0.9rem; }
/* =========================================================================
   HEADER / NAV
   ========================================================================= */
header.site {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgba(246, 245, 240, 0.92);
  backdrop-filter: blur(6px);
  border-bottom: var(--border-soft);
}
.nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding-block: 0.7rem;
}
.logo {
  font-family: var(--font-hand);
  font-size: 1.8rem;
  color: var(--ink);
  display: flex;
  align-items: center;
  gap: 0.4rem;
  transform: rotate(-2deg);
}
.nav-links {
  display: flex;
  align-items: center;
  gap: 1.4rem;
  list-style: none;
}
.nav-links a {
  font-family: var(--font-hand2);
  font-size: 1.15rem;
  color: var(--ink);
  padding: 0.2rem 0.3rem;
  border-radius: 6px;
  transition: color 0.15s, transform 0.15s;
}
.nav-links a:hover { color: var(--marker-blue); transform: rotate(-2deg); }
.nav-right { display: flex; align-items: center; gap: 1rem; }
.nav-toggle {
  display: none;
  background: var(--paper);
  border: var(--border);
  border-radius: 8px;
  padding: 0.4rem 0.6rem;
  font-size: 1.4rem;
  cursor: pointer;
  box-shadow: var(--shadow-hard);
}
/* =========================================================================
   HERO
   ========================================================================= */
.hero {
  /* Exactly one screen: the landing view shows nothing below it.
     svh keeps it honest on mobile, where the browser chrome moves.
     min-height (not height) so short viewports scroll instead of clipping. */
  min-height: calc(100vh - var(--header-h, 62px));
  min-height: calc(100svh - var(--header-h, 62px));
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
  padding-block: clamp(1rem, 4vh, 3rem);
  text-align: center;
}
.hero-inner { max-width: 900px; margin-inline: auto; width: 100%; }
.hero h1 {
  font-size: var(--fs-hero);
  margin-bottom: 1rem;
}
.hero h1 .accent { color: var(--marker-blue); }
/* The pen name, signed after the name rather than sitting on the line with
   it — small, tilted, and in the Devanagari hand. */
.hero h1 .pen-name {
  display: inline-block;
  font-family: var(--font-deva);
  font-size: 0.26em;
  /* Same weight as the poem titles, which is where this hand is used. */
  font-weight: 700;
  color: var(--marker-red);
  /* Sits low, trailing off the end of the name like a signature, rather
     than riding above it as a superscript. */
  vertical-align: baseline;
  margin-left: 0.04em;
  transform: translateY(0.55em) rotate(-7deg);
  letter-spacing: 0.02em;
}
.hero .tagline {
  /* rem, not ch: ch resolves against the body font, which is much narrower
     than the Devanagari line it has to hold. */
  max-width: min(94%, 44rem);
  margin: 0 auto 1.7rem;
}
.hero .tagline .ne {
  /* Highlight is painted as a background so it follows every wrapped line,
     which an absolutely-positioned bar cannot do. */
  display: inline;
  font-family: var(--font-deva);
  font-size: clamp(1.25rem, 2.7vw, 1.9rem);
  line-height: 1.85;
  color: var(--ink);
  padding: 0 0.18em;
  background-image: linear-gradient(transparent 58%, var(--marker-yellow) 58%, var(--marker-yellow) 92%, transparent 92%);
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}
.hero .tagline .en {
  display: block;
  margin-top: 0.5rem;
  font-family: var(--font-hand2);
  font-size: clamp(0.92rem, 1.4vw, 1.08rem);
  color: var(--ink-soft);
  text-wrap: balance;
}
.hero .role-list {
  font-family: var(--font-hand2);
  font-size: clamp(1.2rem, 2.4vw, 1.9rem);
  color: var(--ink-soft);
  margin-bottom: 0.9rem;
}
.hero .role-list span { color: var(--marker-orange); }
.hero .cta-row {
  display: flex;
  gap: 1rem;
  justify-content: center;
  /* Centre rather than stretch: each button carries its own slight tilt, and
     stretching makes the taller one look out of line with the rest. */
  align-items: center;
  flex-wrap: wrap;
}
.hero .scroll-hint {
  position: absolute;
  bottom: clamp(0.75rem, 3vh, 2rem); left: 50%;
  transform: translateX(-50%) rotate(-3deg);
  font-family: var(--font-hand2);
  color: var(--ink-soft);
  font-size: 1rem;
  white-space: nowrap;
}
/* =========================================================================
   PORTFOLIO — filterable gallery
   ========================================================================= */
.filter-strip {
  display: flex;
  gap: 0.8rem;
  flex-wrap: wrap;
  justify-content: center;
  margin-bottom: 1.25rem;
}
.filter-tab {
  font-family: var(--font-hand);
  font-size: 1.1rem;
  padding: 0.45rem 1.1rem;
  border: var(--border);
  border-radius: 8px;
  background: var(--paper);
  cursor: pointer;
  box-shadow: var(--shadow-hard);
  transition: transform 0.15s, background 0.15s, opacity 0.15s;
  transform: rotate(-0.6deg);
}
.filter-tab:hover { transform: rotate(1deg); }
.filter-tab.active {
  background: var(--marker-yellow);
  transform: rotate(-1.2deg) skewX(-2deg);
}
.filter-tab[data-cat="cosplay"].active { background: var(--marker-pink); }
.filter-tab[data-cat="vfx"].active { background: #cfe0f7; }
.filter-tab[data-cat="writer"].active { background: #d8f0dd; }
.filter-tab[data-cat="design"].active { background: #f9dcc0; }
.portfolio-grid {
  --grid-gap: 1.6rem;
  --grid-min: 210px;
  --grid-max-cols: 4;
  display: grid;
  /* Every track is at least a quarter of the row, so the layout can never
     exceed --grid-max-cols however wide the screen gets. The 1fr lets a
     track grow past that, which is what makes one column fill a phone.

     auto-fit rather than auto-fill: when there are fewer cards than columns
     the spare tracks collapse, so a part-filled row centres instead of
     sitting left with a hole beside it. */
  grid-template-columns: repeat(auto-fit, minmax(
    max(var(--grid-min),
        (100% - (var(--grid-max-cols) - 1) * var(--grid-gap)) / var(--grid-max-cols)),
    1fr));
  justify-content: center;
  gap: var(--grid-gap);
}
.work {
  background: var(--paper);
  border: var(--border);
  border-radius: 14px;
  overflow: hidden;
  box-shadow: var(--shadow-hard);
  transition: transform 0.18s ease, box-shadow 0.18s ease;
  /* A grid row is as tall as its tallest card. Without this, a card with no
     caption leaves a blank strip under its image instead of filling. */
  display: flex;
  flex-direction: column;
}
.work .thumb { flex: 1 1 auto; min-height: 0; }
.work.hidden { display: none; }
.work:hover { transform: rotate(0deg) translateY(-6px); box-shadow: var(--shadow-hard-lg); }
.work .thumb {
  aspect-ratio: 4 / 3;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 3.4rem;
  position: relative;
  border-bottom: var(--border);
  overflow: hidden;
}
/* Film stills are 16:9, but the card needs room for the details riding on
   it, so the tile is a little taller than the still and the image covers. */
.work .thumb.has-img { aspect-ratio: 4 / 3; }
/* Photos and clips are mostly shot upright, so gallery tiles are portrait. */
.work .thumb.has-img.portrait { aspect-ratio: 3 / 4; }
/* Landscape cards want room to breathe. */
.portfolio-grid.wide-cards { --grid-min: 300px; --grid-max-cols: 3; }
.work .thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* Clips are often shot on a phone, so they arrive portrait. `contain` letter-
   boxes whatever shape turns up instead of cropping the subject out of frame;
   for a 16:9 reel in a 16:9 box it behaves identically to `cover`. */
.work .thumb video {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
  background: #000;
}
/* The controls belong to the video, not the card - don't let a click on
   play bubble up to any card-level handler. */
.work .thumb video { position: relative; z-index: 1; }
/* Outward links, unlike the reader cards */
a.work-out {
  display: block;
  color: inherit;
  text-decoration: none;
  position: relative;
}
a.work-out:hover,
a.work-out:focus-visible {
  transform: translateY(-4px);
  box-shadow: var(--shadow-hard-lg);
}
a.work-out .out-hint {
  position: absolute;
  top: 10px; right: 10px;
  font-family: var(--font-hand);
  font-size: 0.75rem;
  background: var(--ink);
  color: var(--paper);
  border-radius: 6px;
  padding: 0.1rem 0.5rem;
  opacity: 0;
  transition: opacity 0.18s ease;
}
a.work-out:hover .out-hint,
a.work-out:focus-visible .out-hint { opacity: 1; }
/* Not finished yet */
.work-soon { opacity: 0.72; }
.work-soon .thumb { filter: grayscale(0.6); }
.work-status {
  font-family: var(--font-hand2);
  color: var(--marker-orange);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  font-size: 0.85rem;
}
.work .thumb .cat-pill {
  position: absolute;
  top: 10px; left: 10px;
  font-family: var(--font-hand);
  font-size: 0.95rem;
  background: var(--paper);
  border: 2px solid var(--ink);
  border-radius: 6px;
  padding: 0.2rem 0.65rem;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.work .thumb .featured-badge {
  position: absolute;
  top: 10px; right: 10px;
  font-family: var(--font-hand);
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--marker-red);
  background: var(--paper);
  border: 2px solid var(--marker-red);
  border-radius: 6px;
  padding: 0.1rem 0.55rem;
  transform: rotate(3deg);
  z-index: 4;
}
.work .body { padding: 1rem 1.1rem 1.2rem; }
.work .body h3 { font-size: 1.2rem; margin-bottom: 0.3rem; }
.work .body p { font-size: 0.9rem; color: var(--ink-soft); }
/* thumb backgrounds */
.t-cosplay { background: repeating-linear-gradient(45deg, #ffe0e8 0 12px, #ffd0dc 12px 24px); }
.t-vfx { background: repeating-linear-gradient(-45deg, #dce8f7 0 12px, #cfe0f7 12px 24px); }
.t-writer { background: repeating-linear-gradient(45deg, #e2f3e7 0 12px, #d3edda 12px 24px); }
.t-design { background: repeating-linear-gradient(45deg, #f9dcc0 0 12px, #f4ceab 12px 24px); }
/* =========================================================================
   SHOP
   ========================================================================= */
.shop-tabs {
  display: flex;
  gap: 0.8rem;
  /* Wraps like the portfolio strip. Without this the last tab ran off the
     side of a phone with nothing to say it was there. */
  flex-wrap: wrap;
  justify-content: center;
  margin-bottom: 2rem;
}
.shop-tabs .filter-tab { font-size: 1.2rem; }
.shop-grid {
  --grid-gap: 1.6rem;
  --grid-min: 230px;
  --grid-max-cols: 4;
  display: grid;
  /* Every track is at least a quarter of the row, so the layout can never
     exceed --grid-max-cols however wide the screen gets. The 1fr lets a
     track grow past that, which is what makes one column fill a phone.

     auto-fit rather than auto-fill: when there are fewer cards than columns
     the spare tracks collapse, so a part-filled row centres instead of
     sitting left with a hole beside it. */
  grid-template-columns: repeat(auto-fit, minmax(
    max(var(--grid-min),
        (100% - (var(--grid-max-cols) - 1) * var(--grid-gap)) / var(--grid-max-cols)),
    1fr));
  justify-content: center;
  gap: var(--grid-gap);
}
.product {
  background: var(--paper);
  border: var(--border);
  border-radius: 14px;
  overflow: hidden;
  box-shadow: var(--shadow-hard);
  display: flex;
  flex-direction: column;
  transition: transform 0.18s ease, box-shadow 0.18s ease;
}
.product:hover { transform: rotate(0deg) translateY(-6px); box-shadow: var(--shadow-hard-lg); }
.product .thumb {
  /* The card is the image now, so the thumb owns the whole tile. */
  aspect-ratio: 3 / 4;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 3rem;
  border-bottom: var(--border);
  position: relative;
  overflow: hidden;
  background: var(--bg-alt);
}
.product .thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.product .prod-tag {
  position: absolute;
  top: 10px; left: 10px;
  font-family: var(--font-hand);
  font-size: 0.75rem;
  background: var(--paper);
  border: 2px solid var(--ink);
  border-radius: 6px;
  padding: 0.1rem 0.5rem;
}
.price-status {
  font-family: var(--font-hand2);
  font-size: 0.95rem;
  color: var(--marker-orange);
}
.add-btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  box-shadow: none;
}
.product .body {
  padding: 1rem 1.1rem 1.2rem;
  display: flex;
  flex-direction: column;
  flex-grow: 1;
}
.product .body h3 { font-size: 1.15rem; margin-bottom: 0.3rem; }
.product .body .desc { font-size: 0.88rem; color: var(--ink-soft); flex-grow: 1; margin-bottom: 0.8rem; }
.product .price-row { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; }
.product .price {
  font-family: var(--font-hand);
  font-size: 1.4rem;
  color: var(--marker-red);
  font-weight: bold;
}
.add-btn {
  font-family: var(--font-hand2);
  font-size: 1rem;
  background: var(--marker-green);
  color: #fff;
  border: 2px solid var(--ink);
  border-radius: 8px;
  padding: 0.4rem 0.8rem;
  cursor: pointer;
  box-shadow: 3px 3px 0px var(--ink);
  transition: transform 0.1s, box-shadow 0.1s;
}
.add-btn:hover { transform: translate(-1px, -1px); box-shadow: 4px 4px 0px var(--ink); }
.add-btn:active { transform: translate(2px, 2px); box-shadow: 1px 1px 0px var(--ink); }
.empty-msg {
  text-align: center;
  font-family: var(--font-hand2);
  font-size: 1.2rem;
  color: var(--ink-soft);
  padding: 2rem;
}
.qty { display: flex; align-items: center; gap: 0.3rem; }
.qty button {
  width: 26px; height: 26px;
  border: var(--border);
  border-radius: 6px;
  background: var(--paper);
  font-family: var(--font-hand);
  font-size: 1rem;
  cursor: pointer;
}
.qty button:hover { background: var(--marker-yellow); }
.qty span { font-family: var(--font-hand2); min-width: 20px; text-align: center; }
/* toast */
.toast {
  position: fixed;
  bottom: 24px; left: 50%;
  transform: translateX(-50%) translateY(120px);
  background: var(--ink);
  color: #fff;
  font-family: var(--font-hand2);
  font-size: 1.05rem;
  padding: 0.7rem 1.3rem;
  border-radius: 10px;
  border: 2px solid #fff;
  z-index: 200;
  opacity: 0;
  transition: transform 0.3s ease, opacity 0.3s ease;
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3);
}
.toast.show { transform: translateX(-50%) translateY(0); opacity: 1; }
/* =========================================================================
   CONTACT + FOOTER
   ========================================================================= */
.contact {
  background: var(--paper);
  border: var(--border);
  border-radius: 18px;
  box-shadow: var(--shadow-hard-lg);
  padding: clamp(2rem, 5vw, 3.5rem);
  text-align: center;
  position: relative;
  /* The form inside is capped at 620px, so a full-width card left a wide
     blank margin inside its own border. Sized to the form plus its padding
     instead, and centred in the section. */
  max-width: 51rem;
  margin-inline: auto;
}
.contact h2 { font-size: var(--fs-h2); margin-bottom: 1rem; }
.contact p { font-family: var(--font-hand2); color: var(--ink-soft); font-size: 1.2rem; margin-bottom: 1.6rem; }
/* contact form */
.contact-form {
  max-width: 620px;
  margin: 0 auto 2rem;
  text-align: left;
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
}
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 1.1rem; }
/* Second row of the same grid, kept in the right-hand column so it sits
   directly under Email rather than under Name. */
.form-field .optional { font-style: normal; opacity: 0.6; font-size: 0.85em; }
.form-field { display: flex; flex-direction: column; gap: 0.35rem; }
.form-field span:first-child {
  font-family: var(--font-hand);
  font-size: 1.1rem;
}
.form-field input,
.form-field select,
.form-field textarea {
  font-family: var(--font-hand2);
  font-size: 1.05rem;
  padding: 0.6rem 0.8rem;
  border: var(--border);
  border-radius: 10px;
  background: var(--paper);
  color: var(--ink);
  box-shadow: var(--shadow-hard);
  transition: box-shadow 0.15s, border-color 0.15s;
}
.form-field input:focus,
.form-field select:focus,
.form-field textarea:focus {
  outline: none;
  border-color: var(--marker-blue);
  box-shadow: 4px 4px 0px var(--marker-blue);
}
.form-field textarea { resize: vertical; }
.form-error {
  font-family: var(--font-hand);
  font-size: 0.9rem;
  color: var(--marker-red);
  min-height: 1.1em;
  opacity: 0;
}
.form-field.invalid .form-error { opacity: 1; }
.form-field.invalid input,
.form-field.invalid select,
.form-field.invalid textarea {
  border-color: var(--marker-red);
  box-shadow: 4px 4px 0px var(--marker-red);
}
.form-success {
  font-family: var(--font-hand);
  font-size: 1.15rem;
  color: var(--marker-green);
  background: #e6f6ec;
  border: 2px dashed var(--marker-green);
  border-radius: 10px;
  padding: 0.9rem 1rem;
  display: none;
}
.form-success.show { display: block; }
@media (max-width: 520px) {
  .form-row { grid-template-columns: 1fr; }
}
footer {
  text-align: center;
  padding: 2.5rem 1rem;
  border-top: var(--border-soft);
  font-family: var(--font-hand2);
  color: var(--ink-soft);
}
footer .logo { justify-content: center; margin-bottom: 0.5rem; }
/* =========================================================================
   RESPONSIVE
   ========================================================================= */
@media (max-width: 680px) {
  .nav-links {
    position: fixed;
    top: var(--header-h, 62px); left: 0; right: 0;
    flex-direction: column;
    gap: 0.5rem;
    background: var(--paper);
    border-bottom: var(--border);
    padding: 1rem;
    box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
    transform: translateY(-140%);
    /* Sliding it out of sight is not the same as taking it away. At -140%
       its lower edge still lay over the top of the header, invisible but
       tappable, and it swallowed taps meant for the Cart button. These two
       take it out of hit-testing while it is closed; the delay on
       visibility lets the slide finish before it disappears. */
    visibility: hidden;
    pointer-events: none;
    transition: transform 0.3s ease, visibility 0s linear 0.3s;
    z-index: 40;
  }
  .nav-links.open {
    transform: translateY(0);
    visibility: visible;
    pointer-events: auto;
    transition: transform 0.3s ease, visibility 0s;
  }
  .nav-toggle { display: block; }
}
/* Short viewports (landscape phones): the hint would sit on the buttons. */
@media (max-height: 620px) {
  .hero .scroll-hint { display: none; }
}
/* Phones. The layout already reflows on its own; these are the places where
   text or controls were reaching the glass edge. */
@media (max-width: 430px) {
  .container { width: min(1160px, 100vw - 1.75rem); }
  .role-list { font-size: 0.95rem; line-height: 1.7; }
  .hero .tagline { max-width: 100%; }
  /* Three call-to-action buttons across a phone leaves each of them too
     narrow to read; stacked, each is a comfortable target. */
  .hero .cta-row { flex-direction: column; align-items: stretch; }
  .hero .cta-row .btn { width: 100%; }
  .filter-strip, .shop-tabs { gap: 0.5rem; }
  .shop-tabs .filter-tab { font-size: 1.05rem; }
  /* Space that reads as generous on a wide screen reads as a hole on a
     phone, where everything is stacked in one column. */
  section { padding-block: 2rem; }
  /* One screen on a phone as well: the landing view shows the name and the
     three buttons and nothing else, with the scroll hint at its foot saying
     there is more below. min-height, not height, so a short handset scrolls
     rather than clipping. */
  .hero { padding-block: 1.5rem 3.5rem; }
  .section-head { margin-bottom: 1.4rem; }
  .section-head p { margin-bottom: 0; }
  .hero .tagline { margin-bottom: 1.1rem; }
  .hero .cta-row { gap: 0.7rem; }
  .filter-strip, .shop-tabs { margin-bottom: 1.2rem; }
  #writingSubtabs, #workSubtabs, #workGroups, #writingCats { margin-bottom: 0.8rem; }
  .pager { margin-top: 1.4rem; }
  /* The cart badge hangs 10px outside its own button, so the bar needs
     headroom or the badge reads as sheared off against the top of the
     screen. The logo comes down a size to give all three room. */
  .nav { padding-block: 1rem 0.75rem; }
  .logo { font-size: 1.5rem; }
  .nav-toggle { font-size: 1.25rem; padding: 0.35rem 0.55rem; }
}
/* A stray pixel of sideways scroll makes a phone feel broken even when
   nothing is visibly cut off. `clip`, and on html rather than body: either
   `hidden`, or any of it on body, turns body into a scroll container and the
   sticky header then scrolls away with the page. */
html { overflow-x: clip; }
/* accessibility: reduced motion */
@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
  html { scroll-behavior: auto; }
}
/* Loading state styles */
.loading {
  text-align: center;
  padding: 3rem;
  color: var(--ink-soft);
  font-family: var(--font-hand2);
  font-size: 1.2rem;
}
.loading::after {
  content: '';
  display: inline-block;
  width: 1rem;
  height: 1rem;
  margin-left: 0.5rem;
  border: 2px solid var(--ink-soft);
  border-top-color: var(--marker-blue);
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}
@keyframes spin {
  to { transform: rotate(360deg); }
}
/* --- Writing cards backed by WordPress --- */
.work-link {
  /* Not `display: block` — .work is a flex column so a caption-less card
     lets its image fill the row height instead of leaving a blank strip. */
  color: inherit;
  text-decoration: none;
  cursor: pointer;
}
.work-link:hover .body h3 { color: var(--marker-blue); }
.work-link:focus-visible {
  outline: 3px solid var(--marker-blue);
  outline-offset: 3px;
}
.retry-btn {
  font-family: var(--font-hand2);
  font-size: 1rem;
  background: var(--paper);
  border: var(--border);
  border-radius: 8px;
  padding: 0.2rem 0.7rem;
  margin-left: 0.4rem;
  cursor: pointer;
  box-shadow: 3px 3px 0px rgba(0, 0, 0, 0.15);
}
.retry-btn:hover { background: var(--marker-yellow); }
/* Anchors clear the sticky header */
html { scroll-padding-top: 5.5rem; }
/* --- In-site reader for WordPress posts --- */
.reader-overlay {
  position: fixed;
  top: 0; right: 0; bottom: 0; left: 0;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s ease;
  z-index: 150;
}
.reader-overlay.open { opacity: 1; pointer-events: auto; }
.reader {
  position: fixed;
  top: 50%; left: 50%;
  transform: translate(-50%, -46%) scale(0.98);
  width: min(760px, 92vw);
  max-height: 88vh;
  display: flex;
  flex-direction: column;
  background: var(--paper);
  border: var(--border);
  border-radius: 16px;
  box-shadow: var(--shadow-hard-lg);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s ease, transform 0.25s ease;
  z-index: 160;
}
.reader.open {
  opacity: 1;
  pointer-events: auto;
  transform: translate(-50%, -50%) scale(1);
}
.reader-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.8rem 1.2rem;
  border-bottom: var(--border-soft);
  flex-shrink: 0;
}
.reader-close {
  background: var(--paper);
  border: var(--border);
  border-radius: 8px;
  padding: 0.2rem 0.6rem;
  font-size: 1.1rem;
  cursor: pointer;
  line-height: 1.4;
  box-shadow: 3px 3px 0px rgba(0, 0, 0, 0.15);
  flex-shrink: 0;
}
.reader-close:hover { background: var(--marker-yellow); }
.reader-body {
  overflow-y: auto;
  padding: 1.4rem clamp(1.2rem, 4vw, 2.4rem) 2rem;
  /* Centres the block below; the text inside stays left-aligned. */
  text-align: center;
  /* The scrollbar eats ~15px off the right only, which would throw the
     centring off by that much. Reserving a gutter on both edges keeps the
     left and right margins equal whether or not the post scrolls. */
  scrollbar-gutter: stable both-edges;
}
/* Shrink-wraps to the widest line, so short verse sits centred in the sheet
   with even margins instead of hugging the left edge. Title and body move
   together, so they stay aligned with each other. */
.reader-inner {
  display: inline-block;
  text-align: left;
  max-width: 100%;
}
.reader-tag {
  display: inline-block;
  margin-bottom: 0.7rem;
  font-family: var(--font-hand);
  font-size: 0.9rem;
  background: var(--paper);
  border: 2px solid var(--ink);
  border-radius: 8px;
  padding: 0.15rem 0.6rem;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.reader-tag[hidden] { display: none !important; }
.reader-body h2 {
  font-size: clamp(1.8rem, 3.5vw, 2.6rem);
  margin-bottom: 1.2rem;
  line-height: 1.15;
}
.reader-content {
  font-family: var(--font-body);
  font-size: 1.02rem;
  line-height: 1.75;
  color: var(--ink);
}
.reader-content > * + * { margin-top: 1rem; }
.reader-content p { margin-bottom: 0; }
.reader-content h2,
.reader-content h3,
.reader-content h4 {
  font-family: var(--font-display);
  font-size: 1.5rem;
  margin-top: 1.6rem;
}
.reader-content a { color: var(--marker-blue); text-decoration: underline; }
.reader-content img {
  border: var(--border);
  border-radius: 10px;
  margin-inline: auto;
}
.reader-content blockquote {
  border-left: 4px solid var(--marker-orange);
  padding-left: 1rem;
  color: var(--ink-soft);
  font-style: italic;
}
.reader-content ul,
.reader-content ol { padding-left: 1.4rem; }
.reader-content pre {
  background: var(--bg-alt);
  border: var(--border-soft);
  border-radius: 8px;
  padding: 0.8rem;
  overflow-x: auto;
}
/* Devanagari runs long — give it a little more room to breathe */
.reader-content[lang="ne"] { line-height: 2; font-size: 1.08rem; }
.reader-pos {
  font-family: var(--font-hand2);
  font-size: 0.9rem;
  color: var(--ink-soft);
}
.reader-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.8rem 1.2rem;
  border-top: var(--border-soft);
  background: var(--bg-alt);
  border-radius: 0 0 14px 14px;
  flex-shrink: 0;
}
.reader-nav-btn {
  font-family: var(--font-hand2);
  font-size: 1rem;
  background: var(--paper);
  border: var(--border);
  border-radius: 10px;
  padding: 0.4rem 1rem;
  cursor: pointer;
  box-shadow: 3px 3px 0px rgba(0, 0, 0, 0.15);
  transition: transform 0.15s, background 0.15s;
}
.reader-nav-btn:hover:not(:disabled) {
  background: var(--marker-yellow);
  transform: translateY(-2px);
}
.reader-nav-btn:disabled {
  opacity: 0.35;
  cursor: not-allowed;
  box-shadow: none;
}
@media (max-width: 680px) {
  .reader { width: 96vw; max-height: 92vh; }
}
/* --- Writing sub-tabs --- */
.sub-strip[hidden],
.cat-strip[hidden] { display: none !important; }
.sub-strip {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.5rem;
  margin: -0.6rem 0 1.6rem;
}
.sub-tab {
  font-family: var(--font-hand2);
  font-size: 0.95rem;
  background: transparent;
  border: 2px dashed #bbbbbb;
  border-radius: 20px;
  padding: 0.25rem 0.9rem;
  cursor: pointer;
  color: var(--ink-soft);
  transition: background 0.15s, border-color 0.15s, color 0.15s;
}
.sub-tab:hover { border-color: var(--marker-blue); color: var(--marker-blue); }
.sub-tab.active {
  background: var(--marker-blue);
  border-color: var(--marker-blue);
  border-style: solid;
  color: #fff;
}
/* --- Category chips inside a single writing source --- */
.cat-strip {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  margin: -0.9rem 0 1.6rem;
}
.cat-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.4rem;
}
.cat-row.sub {
  padding-top: 0.5rem;
  border-top: var(--border-soft);
  width: min(680px, 100%);
}
.cat-row.sub .cat-chip { font-size: 0.8rem; border-style: dashed; }
.cat-chip {
  font-family: var(--font-hand2);
  font-size: 0.85rem;
  background: var(--paper);
  border: 1px solid #cfcfcf;
  border-radius: 14px;
  padding: 0.18rem 0.7rem;
  cursor: pointer;
  color: var(--ink-soft);
  transition: background 0.15s, border-color 0.15s, color 0.15s;
}
.cat-chip:hover { border-color: var(--marker-orange); color: var(--marker-orange); }
.cat-chip.active {
  background: var(--marker-yellow);
  border-color: var(--ink);
  color: var(--ink);
}
.cat-chip .n { opacity: 0.55; font-size: 0.78rem; }
/* --- Reader header actions --- */
.reader-actions { display: flex; align-items: center; gap: 0.5rem; flex-shrink: 0; }
.reader-share {
  font-family: var(--font-hand2);
  font-size: 0.9rem;
  background: var(--paper);
  border: var(--border);
  border-radius: 8px;
  padding: 0.2rem 0.7rem;
  cursor: pointer;
  box-shadow: 3px 3px 0px rgba(0, 0, 0, 0.15);
  white-space: nowrap;
}
.reader-share:hover { background: var(--marker-yellow); }
@media (max-width: 480px) {
  .reader-share { font-size: 0; padding: 0.3rem 0.55rem; }
  .reader-share::before { content: '🔗'; font-size: 1rem; }
}
/* --- Page numbers --- */
.pager {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 0.4rem;
  margin-top: 1.2rem;
}
.pager[hidden] { display: none !important; }
.pg {
  font-family: var(--font-hand2);
  font-size: 1rem;
  min-width: 38px;
  padding: 0.3rem 0.6rem;
  background: var(--paper);
  border: var(--border);
  border-radius: 10px;
  cursor: pointer;
  box-shadow: 3px 3px 0px rgba(0, 0, 0, 0.15);
  transition: transform 0.15s, background 0.15s;
}
.pg:hover:not(:disabled):not(.active) {
  background: var(--marker-yellow);
  transform: translateY(-2px);
}
.pg.active {
  background: var(--marker-blue);
  color: #fff;
  cursor: default;
}
.pg:disabled { opacity: 0.35; cursor: not-allowed; box-shadow: none; }
.pg.gap {
  border: none;
  box-shadow: none;
  background: none;
  min-width: 20px;
  cursor: default;
  color: var(--ink-soft);
}
/* --- Lightbox: images and video open like a post --- */
.lightbox {
  position: fixed;
  top: 50%; left: 50%;
  transform: translate(-50%, -46%) scale(0.98);
  /* Sized to the picture, not the other way round. A portrait phone video in
     a fixed landscape panel sat in a wide field of black; the panel now takes
     the shape of whatever is inside it. The minimum keeps the header and the
     prev/next row usable when the picture is very narrow. */
  width: fit-content;
  min-width: min(340px, 94vw);
  max-width: min(980px, 94vw);
  max-height: 92vh;
  display: flex;
  flex-direction: column;
  background: var(--paper);
  border: var(--border);
  border-radius: 16px;
  box-shadow: var(--shadow-hard-lg);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s ease, transform 0.25s ease;
  z-index: 160;
}
.lightbox.open {
  opacity: 1;
  pointer-events: auto;
  transform: translate(-50%, -50%) scale(1);
}
.lb-stage {
  flex: 1;
  min-height: 0;
  /* Holds its ground while the file is still arriving, so the panel does not
     collapse around the spinner and then jump open. */
  min-height: 200px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #111;
  overflow: hidden;
}
.lb-stage img,
.lb-stage video {
  max-width: 100%;
  /* Leaves room for the title row above and the prev/next row below, which
     92vh alone does not on a short screen. */
  max-height: min(74vh, calc(92vh - 9rem));
  width: auto;
  height: auto;
  display: block;
}
.lb-title {
  font-size: 1rem;
  text-align: center;
  flex: 1;
  margin: 0 0.8rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* A poster frame in the grid needs to read as playable */
.work .thumb .play-badge {
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: 54px; height: 54px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  font-size: 1.3rem;
  display: flex;
  align-items: center;
  justify-content: center;
  padding-left: 4px;
  pointer-events: none;
  transition: background 0.18s ease, transform 0.18s ease;
}
.work-link:hover .thumb .play-badge {
  background: rgba(0, 0, 0, 0.75);
  transform: translate(-50%, -50%) scale(1.08);
}
.work-link { cursor: pointer; }
.work-link:hover { transform: translateY(-4px); box-shadow: var(--shadow-hard-lg); }
@media (max-width: 680px) {
  .lb-title { display: none; }
}
/* --- Writing card whose post has a featured image: all picture --- */
.work .thumb.cover {
  /* Upright, like the gallery tiles. Every writing card is a cover, so the
     rows stay even. */
  aspect-ratio: 3 / 4;
}
.work .thumb.cover .cover-title {
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  inset: 0;                     /* fill the card so the title can centre in it */
  display: flex;
  flex-direction: column;       /* verse, then its "read more" beneath it */
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 0.3rem;
  /* The title sits in its own box now, so no scrim is needed behind it. */
  background: none;
  pointer-events: none;
}
/* Boxed like the category tag, so it reads the same way over a photo or
   over blank paper. */
.work .thumb.cover .cover-title h3 {
  display: inline-block;
  max-width: 100%;
  margin: 0;
  color: #000;
  /* Frosted glass. The opaque-ish fallback comes first for browsers with no
     backdrop-filter, where a 45% white box alone would be too faint. */
  background: rgba(255, 255, 255, 0.88);
  border-radius: 10px;
  padding: 0.3rem 0.7rem 0.38rem;
  /* Caveat carries no Devanagari, so Nepali titles were dropping to a plain
     system face. Kalam is handwritten and covers both scripts. */
  font-family: var(--font-deva);
  font-weight: 700;
  font-size: clamp(1.35rem, 2.05vw, 1.95rem);
  line-height: 1.3;
  letter-spacing: 0.01em;
  /* Soft, not the hard offset the bordered cards use — with no outline a
     hard-edged shadow reads as a printing slip. */
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.16);
}
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .work .thumb.cover .cover-title h3 {
    background: rgba(255, 255, 255, 0.7);
    -webkit-backdrop-filter: blur(10px) saturate(1.3);
    backdrop-filter: blur(10px) saturate(1.3);
  }
}
.work-link:hover .thumb.cover .cover-title h3 { color: var(--ink); }
/* Verse in place of a title: same frosted box, but the line breaks are the
   point, so they are preserved. */
.work .thumb.cover .cover-title .cover-verse {
  display: inline-block;
  max-width: 100%;
  margin: 0;
  color: #000;
  background: rgba(255, 255, 255, 0.88);
  border-radius: 10px;
  /* Padding in em so the frame keeps its proportions as fitVerses() scales
     the text up; in rem it would swallow the width a large verse needs. */
  padding: 0.3em 0.3em 0.34em;
  font-family: var(--font-deva);
  /* font-size is set by fitVerses(): the largest the card will hold. */
  font-size: 1.05rem;
  line-height: 1.4;
  text-align: left;
  overflow: hidden;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.16);
}
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .work .thumb.cover .cover-title .cover-verse {
    background: rgba(255, 255, 255, 0.7);
    -webkit-backdrop-filter: blur(10px) saturate(1.3);
    backdrop-filter: blur(10px) saturate(1.3);
  }
}
/* One verse line, one row. The poem's line breaks are the point, so a line
   never wraps: fitVerses() sizes the text to the widest one instead. */
.work .thumb.cover .cover-title .cover-verse .vl {
  display: block;
  white-space: nowrap;
}
/* Only when even the smallest size will not hold the line. */
.work .thumb.cover .cover-title .cover-verse.wrapped .vl {
  white-space: normal;
  text-indent: -0.85em;
  padding-left: 0.85em;
}
.work .thumb.cover .cover-title .cover-verse .vl:empty { height: 0.6em; }
.work .thumb.cover-plain .cover-title .cover-verse { box-shadow: none; }
/* Every writing card carries this: the card only ever shows an opening.
   Pinned to the foot of the card, clear of the centred title. */
.work .thumb.cover .read-more {
  /* Near the foot of the card, clear of the title or verse centred above it.
     The dashed frame is the same hand as the category chips. */
  position: absolute;
  left: 50%;
  bottom: 0.8rem;
  transform: translateX(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.35em;
  padding: 0.3rem 0.7rem 0.34rem;
  border: 2px dashed #bbbbbb;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.7);
  font-family: var(--font-hand2);
  font-size: 0.8rem;
  color: var(--ink);
  letter-spacing: 0.02em;
  white-space: nowrap;
  pointer-events: none;
}
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .work .thumb.cover .read-more {
    background: rgba(255, 255, 255, 0.55);
    -webkit-backdrop-filter: blur(8px) saturate(1.2);
    backdrop-filter: blur(8px) saturate(1.2);
  }
}
/* On blank paper the frame alone is enough; the glass panel would show as a
   faint grey smear against white. */
.work .thumb.cover-plain .read-more { background: none; backdrop-filter: none; }
.work .thumb.cover .read-more .rm-ne { font-family: var(--font-deva); }
.work .thumb.cover .read-more .rm-sep { opacity: 0.55; }
.work-link:hover .read-more,
.work-link:focus-visible .read-more { color: var(--marker-blue); }
/* Over a photo the prompt needs its own backing to stay legible. */
.work .thumb.cover:not(.cover-plain) .read-more {
  background: rgba(255, 255, 255, 0.85);
  border-radius: 8px;
  padding: 0.12rem 0.4rem;
}
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .work .thumb.cover:not(.cover-plain) .read-more {
    background: rgba(255, 255, 255, 0.7);
    -webkit-backdrop-filter: blur(8px) saturate(1.2);
    backdrop-filter: blur(8px) saturate(1.2);
  }
}
/* Keep the centred title clear of the prompt below it. */
.work .thumb.cover .cover-title { padding-bottom: 2.2rem; }
/* No featured image: same card, plain paper behind the title. */
.work .thumb.cover-plain {
  background: var(--paper);
  background-image: none;
}
.work .thumb.cover-plain .cover-title {
  background: none;             /* nothing to scrim against */
}
.work .thumb.cover-plain .cover-title h3 {
  box-shadow: none;             /* nothing behind it to lift off */
}
/* Honeypot. Off-screen rather than display:none — some bots skip hidden
   inputs, but follow the DOM. Real users never reach it (tabindex -1). */
.hp-field {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}
/* --- Order form --- */
.order-modal {
  position: fixed;
  top: 50%; left: 50%;
  transform: translate(-50%, -46%) scale(0.98);
  width: min(560px, 94vw);
  max-height: 92vh;
  display: flex;
  flex-direction: column;
  background: var(--paper);
  border: var(--border);
  border-radius: 16px;
  box-shadow: var(--shadow-hard-lg);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.25s ease, transform 0.25s ease;
  z-index: 160;
}
.order-modal.open {
  opacity: 1;
  pointer-events: auto;
  transform: translate(-50%, -50%) scale(1);
}
.order-modal .reader-head h3 { margin: 0; font-size: 1.3rem; }
.order-body {
  overflow-y: auto;
  padding: 1.2rem clamp(1rem, 3vw, 1.6rem) 1.5rem;
  scrollbar-gutter: stable;
}
.order-summary {
  background: var(--bg-alt);
  border: var(--border-soft);
  border-radius: 12px;
  padding: 0.8rem 1rem;
  margin-bottom: 1.3rem;
  font-family: var(--font-hand2);
}
.order-summary .os-line {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.18rem 0;
  font-size: 0.95rem;
}
.order-summary .os-total {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 0.5rem;
  padding-top: 0.5rem;
  border-top: var(--border-soft);
  font-size: 1.05rem;
  font-weight: 700;
}
.order-form .optional {
  font-style: normal;
  opacity: 0.6;
  font-size: 0.85em;
}
.order-form button[type="submit"] { width: 100%; margin-top: 0.4rem; }
.order-note {
  margin-top: 0.7rem;
  font-family: var(--font-hand2);
  font-size: 0.85rem;
  color: var(--ink-soft);
  text-align: center;
}
@media (max-width: 560px) {
  .order-modal .form-row { grid-template-columns: 1fr; }
}
/* Handle box, revealed only when a social platform is the chosen route. */
/* .form-field is display:flex, which would otherwise beat the attribute. */
.form-field[hidden] { display: none !important; }
/* --- Empty tab --- */
.empty-state {
  /* The grid would otherwise drop this into one narrow column. */
  grid-column: 1 / -1;
  text-align: center;
  padding: 2.5rem 1rem 3rem;
}
.empty-state .empty-msg {
  margin: 0 0 0.4rem;
  font-family: var(--font-display);
  font-size: clamp(1.4rem, 2.6vw, 1.9rem);
  color: var(--ink);
}
.empty-state .empty-sub {
  margin: 0 0 1.1rem;
  font-family: var(--font-hand2);
  color: var(--ink-soft);
}
.empty-links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  justify-content: center;
}
.btn-ghost {
  font-family: var(--font-hand2);
  font-size: 0.95rem;
  background: var(--paper);
  border: var(--border);
  border-radius: 20px;
  padding: 0.35rem 1rem;
  cursor: pointer;
  box-shadow: 3px 3px 0 rgba(0, 0, 0, 0.13);
  transition: transform 0.15s, background 0.15s;
}
.btn-ghost:hover {
  background: var(--marker-yellow);
  transform: translateY(-2px);
}
/* --- Shop cards: details ride on the image, like the writing cards --- */
.product {
  display: flex;
  flex-direction: column;
}
.product .thumb { flex: 1 1 auto; min-height: 0; }
.prod-overlay {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  padding: 0.7rem 0.75rem 0.75rem;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  text-align: left;
  /* Opaque-ish fallback first, for browsers with no backdrop-filter. */
  background: rgba(255, 255, 255, 0.9);
}
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .prod-overlay {
    background: rgba(255, 255, 255, 0.72);
    -webkit-backdrop-filter: blur(12px) saturate(1.3);
    backdrop-filter: blur(12px) saturate(1.3);
  }
}
.prod-overlay h3 {
  margin: 0;
  font-family: var(--font-deva);
  font-weight: 700;
  font-size: clamp(0.98rem, 1.35vw, 1.15rem);
  line-height: 1.25;
  color: #000;
}
.prod-overlay .desc {
  margin: 0;
  font-family: var(--font-hand2);
  font-size: 0.85rem;
  line-height: 1.35;
  color: var(--ink);
  /* Two lines at most, so every overlay ends up the same height and the
     cards line up across the row. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.prod-overlay .price-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  margin-top: 0.15rem;
}
.prod-overlay .price {
  font-family: var(--font-hand);
  font-size: 1.05rem;
  color: var(--marker-red);
}
.prod-overlay .price-status {
  font-family: var(--font-hand2);
  font-size: 0.9rem;
  color: var(--marker-orange);
}
.prod-overlay .add-btn { flex: 0 0 auto; }
/* The overlay on a linked credit sits over the still, like the shop cards. */
a.work-out .prod-overlay { text-align: left; }
a.work-out .prod-overlay h3 {
  font-family: var(--font-display);
  font-size: clamp(1.05rem, 1.5vw, 1.3rem);
}
a.work-out .prod-overlay .desc {
  /* Three lines here: a credit's blurb carries more than a product's. */
  -webkit-line-clamp: 3;
}
/* The hover hint would collide with the overlay, and the card now says
   plainly enough that it opens something. */
a.work-out .out-hint { display: none; }
.card-share {
  position: absolute;
  top: 0.45rem;
  right: 0.45rem;
  z-index: 3;
  display: grid;
  place-items: center;
  width: 1.9rem;
  height: 1.9rem;
  border-radius: 50%;
  font-size: 0.85rem;
  line-height: 1;
  cursor: pointer;
  color: var(--ink);
  background: rgba(255, 255, 255, 0.82);
  box-shadow: 0 1px 5px rgba(0, 0, 0, 0.18);
  /* Visible but quiet: a hover-only control is one most visitors never find. */
  opacity: 0.5;
  transition: opacity 0.15s ease, transform 0.15s ease;
}
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .card-share {
    background: rgba(255, 255, 255, 0.62);
    -webkit-backdrop-filter: blur(8px) saturate(1.3);
    backdrop-filter: blur(8px) saturate(1.3);
  }
}
.card-share:hover { background: rgba(255, 255, 255, 0.95); }
@media (hover: none) {
  .card-share { opacity: 0.75; }
}
/* Where a shared link lands on a card that opens somewhere else. */
.product.linked,
.work.linked {
  outline: 2px solid var(--accent, #d64545);
  outline-offset: 2px;
}
/* Older phones — Android WebView before Chrome 88, iOS before 15 — have no
   aspect-ratio, and every card tile collapses to nothing at all. The padding
   trick holds the same shapes there, and newer browsers never see it. */
@supports not (aspect-ratio: 1 / 1) {
  .work .thumb,
  .work .thumb.has-img {
    height: 0;
    padding-bottom: 75%;                 /* 4:3 */
  }
  .work .thumb.has-img.portrait,
  .work .thumb.cover,
  .product .thumb {
    height: 0;
    padding-bottom: 133.333%;            /* 3:4 */
  }
  .product .thumb { position: relative; }
  .work .thumb > img,
  .work .thumb > video,
  .product .thumb > img {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
  }
}
/* =========================================================================
   LOADING
   Three kinds: the first-paint cover, skeletons standing in for content
   that is on its way, and images fading up as they arrive.
   ========================================================================= */

/* --- First paint --- */
.boot {
  position: fixed;
  top: 0; right: 0; bottom: 0; left: 0;
  inset: 0;
  z-index: 200;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.4rem;
  background: var(--bg);
  /* The script takes it away at two seconds. This is the safety net for a
     browser where the script never runs at all, so it waits a little longer
     than that rather than racing it. */
  animation: bootOut 0.45s ease 2.6s forwards;
}
.boot.done { animation: bootOut 0.45s ease forwards; }
.boot-mark {
  font-family: var(--font-title, var(--font-hand2));
  font-size: clamp(2.4rem, 7vw, 3.6rem);
  color: var(--ink);
  letter-spacing: 0.01em;
}
.boot-pen {
  font-family: var(--font-deva);
  font-weight: 700;
  font-size: 0.26em;
  color: var(--marker-red);
  display: inline-block;
  transform: translateY(0.55em) rotate(-7deg);
  margin-left: 0.04em;
}
/* A line drawn across, in the same hand-drawn spirit as the rest. */
.boot-bar {
  width: min(220px, 50vw);
  height: 3px;
  border-radius: 3px;
  background: rgba(0, 0, 0, 0.08);
  overflow: hidden;
}
.boot-bar span {
  display: block;
  width: 40%;
  height: 100%;
  border-radius: 3px;
  background: var(--marker-blue);
  animation: bootSlide 1.1s ease-in-out infinite;
}
@keyframes bootSlide {
  0%   { transform: translateX(-110%); }
  100% { transform: translateX(260%); }
}
@keyframes bootOut {
  to { opacity: 0; visibility: hidden; }
}
.sk-block {
  background: linear-gradient(
    100deg,
    rgba(0, 0, 0, 0.05) 30%,
    rgba(0, 0, 0, 0.02) 50%,
    rgba(0, 0, 0, 0.05) 70%
  );
  background-size: 220% 100%;
  animation: shimmer 1.4s ease-in-out infinite;
  border-radius: 6px;
}
.work.skeleton .sk-thumb {
  aspect-ratio: 3 / 4;
  border-radius: 0;
}
/* Reader body, while the post text is on its way. */
.reader-skeleton .sk-line {
  height: 0.9rem;
  margin-bottom: 0.85rem;
}
.reader-skeleton .sk-line:nth-child(3n) { width: 72%; }
.reader-skeleton .sk-line:nth-child(4n) { width: 88%; }
@keyframes shimmer {
  0%   { background-position: 180% 0; }
  100% { background-position: -80% 0; }
}
/* --- Images fading up ---
   A photo that snaps in at full opacity reads as a glitch on a slow
   connection; arriving is gentler. Only images that were still loading get
   the transition, so a cached grid paints instantly. */
.work .thumb img,
.product .thumb img {
  transition: opacity 0.35s ease;
}
.work .thumb img.is-pending,
.product .thumb img.is-pending { opacity: 0; }
/* --- Lightbox, while the full-size file arrives --- */
.lb-stage .lb-spinner {
  width: 2.2rem;
  height: 2.2rem;
  border: 3px solid rgba(0, 0, 0, 0.12);
  border-top-color: var(--marker-blue);
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}
/* --- Buttons mid-send --- */
.btn.is-sending,
button.is-sending { position: relative; }
.btn.is-sending::after,
button.is-sending::after {
  content: '';
  display: inline-block;
  width: 0.85em;
  height: 0.85em;
  margin-left: 0.5em;
  vertical-align: -0.1em;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: spin 0.7s linear infinite;
}
/* Nothing here is information, only reassurance, so none of it needs to
   move for someone who has asked for stillness. */
@media (prefers-reduced-motion: reduce) {
  .boot-bar span, .sk-block, .lb-spinner,
  .btn.is-sending::after, button.is-sending::after { animation: none; }
  .boot { animation: bootOut 0.01s linear 2.6s forwards; }
}
/* The drawn icons that replaced the picture-characters. They take the
   colour of the text around them, so they sit in the page rather than on
   top of it. */
.ico {
  width: 1em;
  height: 1em;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  display: block;
}
.card-share .ico { width: 0.95rem; height: 0.95rem; }
.reader-share .ico {
  display: inline-block;
  vertical-align: -0.12em;
  margin-right: 0.35em;
}
/* Above phone width the tracks take a fixed size rather than stretching.
   With auto-fit collapsing the empty ones, a 1fr track meant a single card
   on the last page swelled to the full width of the row - 1042px across and
   1388px tall, against 241px for its neighbours. A card is now the same size
   whether there are four on the page or one. */
@media (min-width: 431px) {
  /* Tracks stretch to fill the row again. Pinning them to a fixed size meant
     that on a tablet, where the screen is too narrow for the full column
     count, cards sat at their minimum width with 220px of empty paper beside
     them.

     The cap below is what stops a lone card on a last page swelling to the
     whole row - it is generous enough never to bite when two or more cards
     share the row, so both cases come out right. */
  /* margin-inline, not justify-items: centre. Centring the items stops them
     stretching to their track, and a card with no image has no width of its
     own - the writing cards collapsed to six pixels. An auto margin centres
     the card only once the cap has actually narrowed it. */
  .portfolio-grid > *,
  .shop-grid > * {
    /* width first: an auto margin alone makes a grid item shrink to its
       content, and a writing card with no photo has no content width - those
       collapsed to six pixels. A definite width stops that, the cap then
       narrows a lone card, and the auto margin centres what is left. */
    width: 100%;
    /* --card-w is what a card measures when its row is full, published by
       sizeCards() in js/main.js. Capping at it keeps a card the same size
       whether four share the row or one sits on the last page alone. The
       fallback only applies before the first render. */
    max-width: var(--card-w, 26rem);
    margin-inline: auto;
  }
}
/* =========================================================================
   SEARCH  (Writing)
   ========================================================================= */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}
/* The `hidden` attribute only hides through the browser's own stylesheet, so
   any rule of ours that sets display beats it and the element stays on
   screen. The search box was showing on every tab because of this. */
.writing-search[hidden],
#wSearchClear[hidden] { display: none !important; }
.writing-search {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: min(28rem, 100%);
  margin: 0 auto 1.1rem;
}
.writing-search input {
  flex: 1;
  min-width: 0;
  font-family: var(--font-hand2);
  font-size: 1rem;
  padding: 0.5rem 2.2rem 0.5rem 0.9rem;
  border: var(--border);
  border-radius: 999px;
  background: #fff;
  color: var(--ink);
  box-shadow: var(--shadow-hard);
}
.writing-search input::placeholder { color: var(--ink-soft); opacity: 0.85; }
.writing-search input:focus-visible {
  outline: 2px solid var(--marker-blue);
  outline-offset: 2px;
}
/* The browser's own clear cross sits oddly inside a rounded field. */
.writing-search input::-webkit-search-cancel-button { display: none; }
#wSearchClear {
  position: absolute;
  right: 0.5rem;
  width: 1.5rem;
  height: 1.5rem;
  display: grid;
  place-items: center;
  border: none;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.08);
  color: var(--ink);
  font-size: 0.7rem;
  cursor: pointer;
}
#wSearchClear:hover { background: rgba(0, 0, 0, 0.16); }
.search-count {
  font-family: var(--font-hand2);
  font-size: 0.9rem;
  color: var(--ink-soft);
  white-space: nowrap;
}
@media (max-width: 430px) {
  .writing-search { margin-bottom: 0.8rem; }
  .search-count { display: none; }
}
/* =========================================================================
   PHONE DECK
   A phone showed one tall card with a pager under it, so reaching the
   twenty-ninth poem meant twenty-eight taps on a small button. The row
   becomes a deck instead: the whole set side by side, swiped through with
   the browser's own scrolling, which keeps its momentum and its rubber-band
   at either end. The pager disappears because there is nothing left to page.
   ========================================================================= */
@media (max-width: 430px) {
  .portfolio-grid,
  .shop-grid {
    display: flex;
    grid-template-columns: none;      /* leaves the grid rules behind */
    overflow-x: auto;
    overflow-y: visible;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    /* The grid rules centre a part-filled row. On an overflowing flex row
       that centres the whole strip instead, spilling half of it off to the
       left where scrolling cannot reach: the first card sat at -3090px and
       only half the deck could be swiped to. */
    justify-content: flex-start;
    /* Room for the cards' own drop shadows, which a scroll container clips. */
    padding-block: 0.35rem 0.9rem;
    /* No bleeding past the container: pulling the strip out to the screen
       edge with a negative margin made the whole page scroll sideways, which
       reads as broken on a phone even when nothing is visibly cut off. The
       cards are under full width, so a neighbour still peeks in. */
    gap: 0.9rem;
    scrollbar-width: none;
  }
  .portfolio-grid::-webkit-scrollbar,
  .shop-grid::-webkit-scrollbar { display: none; }

  .portfolio-grid > *,
  .shop-grid > * {
    /* A little under full width, so the next card shows at the edge and says
       there is more to swipe to. The desktop cap does not apply here, so
       these have to set their own width back. */
    flex: 0 0 86%;
    width: auto;
    max-width: 86%;
    margin-inline: 0;
    /* start, not center: a centred first card has no valid snap position at
       scroll zero, and mandatory snapping then threw the deck to its far end
       the moment it rendered - it opened on the last card. */
    scroll-snap-align: start;
  }

  /* Nothing to page through once the whole set is in the deck. */
  .portfolio-grid + .pager,
  .shop-grid + .pager { display: none; }

  /* The empty state is a message, not a card, and should not be swiped. */
  .portfolio-grid > .empty-state,
  .shop-grid > .empty-state {
    flex: 0 0 100%;
    max-width: 100%;
  }
}

/* ---------------- How many ---------------- */
.order-qty {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin: 0.9rem 0 0.2rem;
}
.order-qty .oq-label {
  font-family: var(--font-hand);
  font-size: 1.05rem;
  color: var(--ink);
}
.oq-control {
  display: flex;
  align-items: center;
  gap: 0.35rem;
}
.oq-control button {
  width: 2.1rem;
  height: 2.1rem;
  display: grid;
  place-items: center;
  font-family: var(--font-hand);
  font-size: 1.15rem;
  line-height: 1;
  background: var(--paper);
  border: var(--border);
  border-radius: 10px;
  box-shadow: var(--shadow-hard);
  cursor: pointer;
}
.oq-control button:hover:not(:disabled) { background: #fff; }
.oq-control button:disabled { opacity: 0.4; cursor: default; box-shadow: none; }
.oq-control input {
  width: 3.2rem;
  text-align: center;
  font-family: var(--font-hand);
  font-size: 1.05rem;
  padding: 0.4rem 0.2rem;
  border: var(--border);
  border-radius: 10px;
  background: #fff;
  color: var(--ink);
}
/* The spinner arrows are tiny and redundant beside the two buttons. */
.oq-control input::-webkit-outer-spin-button,
.oq-control input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.oq-control input { -moz-appearance: textfield; appearance: textfield; }


/* =========================================================================
   DECK PROGRESS
   A thin line under the deck saying there is more than what is on screen,
   and roughly where you are in it. One dot per card was the obvious thing
   and it was wrong: twenty-nine of them wrapped onto two rows and read as
   clutter. A bar says the same thing at any length.
   ========================================================================= */
.deck-progress {
  display: none;                      /* only the deck has one */
  position: relative;
  height: 3px;
  margin: 0.9rem auto 0;
  width: min(9rem, 45%);
  border-radius: 3px;
  background: rgba(0, 0, 0, 0.1);
  overflow: hidden;
}
.deck-progress[hidden] { display: none !important; }

.deck-progress span {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  border-radius: 3px;
  background: var(--marker-blue);
  /* Follows the finger rather than easing behind it. */
  transition: transform 0.12s linear;
  will-change: transform;
}

@media (max-width: 430px) {
  .deck-progress { display: block; }
}

/* =========================================================================
   FILLING THE SCREEN
   The lightbox already sizes itself to the picture. This drops the paper
   frame and gives the whole viewport to the photo - useful for a wide
   landscape shot on a phone, and the only way to see detail in a large one.
   Done with our own layout rather than the Fullscreen API alone, because
   Safari on iPhone has no Fullscreen API for images at all.
   ========================================================================= */
.lightbox.is-full {
  top: 0;
  left: 0;
  transform: none;
  width: 100vw;
  max-width: none;
  height: 100vh;
  height: 100dvh;
  max-height: none;
  border: none;
  border-radius: 0;
  background: #000;
  box-shadow: none;
}
.lightbox.is-full .reader-head,
.lightbox.is-full .reader-nav {
  background: rgba(0, 0, 0, 0.55);
  border-color: rgba(255, 255, 255, 0.16);
  color: #fff;
}
.lightbox.is-full .reader-head .reader-pos,
.lightbox.is-full .reader-share,
.lightbox.is-full .reader-close,
.lightbox.is-full .reader-nav-btn { color: #fff; }
.lightbox.is-full .reader-share,
.lightbox.is-full .reader-close,
.lightbox.is-full .reader-nav-btn {
  background: rgba(255, 255, 255, 0.12);
  border-color: rgba(255, 255, 255, 0.3);
  box-shadow: none;
}
.lightbox.is-full .lb-stage { background: #000; }
.lightbox.is-full .lb-stage img,
.lightbox.is-full .lb-stage video {
  max-height: none;
  max-width: 100%;
  width: auto;
  height: 100%;
  object-fit: contain;
}
