/* ==========================================================================
   SKETCH WALL — the daily-practice page.
   Organic scatter of sketches, always-alive ambient drift with a shared
   breathing pulse, cursor/tilt proximity reactions, gallery spotlight-dim
   on hover, click to expand. Driven by CONTENT.sketchwall.items.
   ========================================================================== */

html, body {
  overflow: hidden;
  height: 100%;
}

/* ---------- Header — same editorial frame as the homepage ---------- */

.wall-frame-top {
  position: fixed;
  top: 0; left: 0; right: 0;
  /* Cards get a rising z-index every time they're hovered, dragged, or
     rescattered (topZ climbs with no ceiling), and already passes 30
     after the initial 25-card load. A real, confirmed bug: a card can
     end up physically on top of the nav or the rescatter button,
     silently eating clicks meant for them. Fixed UI chrome needs a
     z-index that stays above that climb for the life of the page. */
  z-index: 9000;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 42px var(--side-padding-desktop) 30px;
  pointer-events: none;
}

.wall-frame-top a { pointer-events: auto; }

.wall-mark {
  font-size: var(--fs-body);
  font-weight: var(--fw-medium);
  letter-spacing: 0.01em;
  color: var(--color-text-primary);
  text-decoration: none;
  /* Vertical-only padding + matching negative margin: grows the tap
     height toward 44px without touching the text's horizontal box —
     that matters here specifically because the desktop rule below
     centers this link with left:50% + translateX(-50%), which only
     stays exact if the box's width (and therefore its horizontal
     margin) is left alone. */
  padding: 9px 0;
  margin: -9px 0;
}

@media (min-width: 769px) {
  .wall-mark {
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
  }
}

.wall-frame-links {
  display: flex;
  gap: var(--space-4);
  margin-left: auto;
}

.wall-frame-link {
  font-size: var(--fs-caption);
  font-weight: var(--fw-medium);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-text-primary);
  text-decoration: none;
  opacity: 0.8;
  transition: opacity var(--duration-hover) ease;
  /* Same vertical-only tap-height fix as .wall-mark. */
  padding: 12px 0;
  margin: -12px 0;
}

.wall-frame-link:hover { opacity: 1; }

@media (max-width: 768px) {
  .wall-frame-top { padding: 22px var(--side-padding-mobile); }
}

/* ---------- Headline ---------- */

.wall-headline-wrap {
  position: fixed;
  top: 15vh;
  left: 0; right: 0;
  z-index: 5;
  text-align: center;
  pointer-events: none;
  opacity: 1;
  transition: opacity 0.3s ease;
}

/* While a sketch is open full-size the headline sat behind the veil at a
   low z-index with nothing opaque covering it, so it showed straight
   through above the enlarged image instead of the name/About row being
   the only thing up top. Fade it out with the same open/close move,
   leaving .wall-frame-top (name + About, z-index 9000) untouched. */
.wall-is-expanded .wall-headline-wrap {
  opacity: 0;
}

.wall-headline {
  font-size: clamp(28px, 4.2vw, 56px);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-heading);
  line-height: 1.15;
  color: var(--color-text-primary);
  /* Balances the wrap on narrow screens instead of leaving one or two
     stray words on their own line. */
  text-wrap: balance;
}

.wall-headline .muted { color: var(--color-text-secondary); }

.wall-hint {
  position: fixed;
  bottom: 4vh;
  bottom: 4dvh;
  left: 50%;
  transform: translateX(-50%);
  z-index: 5;
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-text-secondary);
  opacity: 0;
  transition: opacity 1s ease;
  pointer-events: none;
}

.wall-hint.is-visible { opacity: 0.7; }

/* ---------- Field ---------- */

.wall-field {
  position: absolute;
  inset: 0;
}

/* Note: this page used to pin sketches to the wall with a .wall-sketch
   card + a metal pin + a shine hover sweep. That markup was replaced by
   the scatter-card physics gallery below. The old rules lived on here
   unused for a while (dead CSS, never referenced by anything in the
   current markup or JS) — removed outright rather than left dormant,
   since the shine effect specifically must never come back on this
   page and a leftover rule is exactly the kind of thing that gets
   accidentally reactivated later. */

/* ---------- Expanded view ---------- */

.wall-expand-veil {
  position: fixed;
  inset: 0;
  z-index: 100;
  /* No full-page dim/blur here anymore — that used to wash out every
     sketch parted into the side margins along with the center one,
     when only the center image actually needs separation from what's
     behind it. This layer is now just an invisible click-catcher (for
     click-outside-to-close) and a flex container to center the card;
     .wall-expand-backdrop below is what actually provides contrast,
     and only right behind the enlarged image itself. */
  background: transparent;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.35s ease;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
}

.wall-expand-veil.is-active { opacity: 1; pointer-events: all; }

/* Removed the separate backdrop panel — sized well past the card, it
   read as a big extra white border sitting around the actual sketch
   instead of the sketch just floating on its own. The card's own
   box-shadow below (already there) gives it plenty of separation from
   what's behind it without adding a second visible shape. */

.wall-expand-card {
  width: min(72vw, 560px);
  height: min(72vw, 560px);
  border-radius: var(--radius-image);
  box-shadow: 0 40px 100px -30px rgba(17,17,17,0.3);
  background-size: contain;
  background-position: center;
  background-repeat: no-repeat;
  background-color: var(--color-bg);
  outline: 1px solid rgba(17,17,17,0.08);
  outline-offset: -1px;
  transform: scale(0.92);
  transition: transform 0.4s var(--ease-cinematic);
}

.wall-expand-veil.is-active .wall-expand-card { transform: scale(1); }

.wall-expand-title {
  font-size: var(--fs-caption);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--color-text-secondary);
}

.wall-expand-close {
  position: fixed;
  top: 30px; right: 40px;
  z-index: 101;
  /* Padding turns the click target from just the "Close ✕" glyphs
     (well under 44px) into a real, comfortably-sized tap area, without
     changing where the text itself sits on screen — the text was
     already inset from the true corner, this just makes the invisible
     area around it clickable too. */
  padding: 16px 20px;
  margin: -16px -20px;
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  /* --color-text-secondary (#888 on white) measures 3.54:1, under the
     4.5:1 WCAG AA floor for 12px text — and this is the only visible
     way to close the expand view, not a decorative caption. Mixing
     text-primary down to 60% keeps the same muted feel without a new
     hex value outside the locked palette, and clears AA at ~4.9:1. */
  color: color-mix(in srgb, var(--color-text-primary) 60%, white);
  background: none;
  border: none;
  font-family: inherit;
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s ease, color 0.2s ease, transform 0.15s ease;
}

.wall-expand-close.is-active { opacity: 1; pointer-events: all; }
/* Hover/press feedback — the button previously had a focus ring but
   nothing acknowledged a mouse hover or an actual click, the only
   other interactive element on this page without either. */
.wall-expand-close:hover { color: var(--color-text-primary); }
.wall-expand-close:active { transform: scale(0.94); }

/* Reduced motion: the physics-driven scatter, throw and rescatter
   animation is handled in JS (it checks the same media query and
   substitutes an instant cut for the automatic large motion — the
   entrance stagger and the rescatter burst). What's left for CSS to
   handle is the smaller, decorative eased transitions still in use
   across the page (hover scale, expand/close fades, button states) —
   those are dropped here so nothing on this page moves further than
   the user's system setting allows. */
@media (prefers-reduced-motion: reduce) {
  .scatter-card,
  .wall-expand-veil,
  .wall-expand-card,
  .wall-expand-close,
  .wall-headline-wrap,
  .rescatter-btn,
  .rescatter-btn svg,
  .drag-cursor,
  .below a {
    transition: none !important;
    animation: none !important;
  }
}

/* ==========================================================================
   SCATTER GALLERY — paper-airplane physics version, replaces the pinned wall
   ========================================================================== */
  * { box-sizing:border-box; margin:0; padding:0; }
  html, body { height:100%; overflow:hidden; background:var(--color-bg); font-family:var(--font-primary); color:var(--color-text-primary); -webkit-font-smoothing:antialiased; }


  .canvas-bg {
    position:absolute; inset:0;
    background-image: radial-gradient(circle, #DEDCD7 1.2px, transparent 1.2px);
    background-size: 26px 26px;
  }

  .field { position:absolute; inset:0; }

  .scatter-card {
    position:absolute;
    left:0; top:0;
    border-radius:0;
    /* contain, not cover — the whole sketch stays visible, nothing gets
       cropped off the edges. Trade-off: a sketch whose aspect ratio
       doesn't match the card shows a plain white margin on two sides
       instead of filling edge-to-edge. That's the deliberate choice here. */
    background-size:contain; background-position:center; background-repeat:no-repeat;
    background-color:var(--color-bg);
    box-shadow:0 10px 24px -10px rgba(17,17,17,0.22);
    /* A hairline inset edge so a sketch with a white or near-white
       margin doesn't blend straight into the page background — every
       card gets a defined edge, not just the ones with dark content. */
    outline: 1px solid rgba(17,17,17,0.08);
    outline-offset: -1px;
    will-change:transform;
    touch-action:none;
    opacity:0;
    transform:scale(0.3);
    /* A native cursor icon answers "can I drag this?" before any custom
       pill/label does — universally understood, and it responds the
       instant the pointer goes down (Response: feedback on press, not
       on release). Meaningless on touch, harmless there too — there's
       no cursor to show it on. */
    cursor: grab;
  }
  .scatter-card.is-dragging {
    box-shadow:0 30px 60px -18px rgba(17,17,17,0.38);
    cursor: grabbing;
  }
  /* Cards are keyboard-focusable (see sketchwall.js) so this needs a
     real visible ring, not the browser default lost against a photo
     background. Only shows for keyboard focus, not a mouse click. */
  .scatter-card:focus-visible {
    outline: 2px solid var(--color-text-primary);
    outline-offset: 3px;
  }

  @media (hover: none), (pointer: coarse) {
    .drag-cursor { display: none !important; }
  }

  /* One-time hint for touch devices — the hover-driven "Click to view" /
     "Drag" pill has no equivalent on touch (no hover state), so without
     this a touch visitor has zero indication either gesture exists.
     Static (doesn't follow the finger), shown once per browser via
     localStorage, then gone for good — see sketchwall.js. */
  .wall-touch-hint {
    position: fixed;
    left: 50%;
    top: max(78px, env(safe-area-inset-top, 0px) + 62px);
    transform: translateX(-50%);
    z-index: 60;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: 999px;
    padding: 9px 18px;
    font-size: var(--fs-caption);
    color: var(--color-text-secondary);
    white-space: nowrap;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.4s ease;
  }
  .wall-touch-hint.is-visible { opacity: 1; }

  @media (hover: none), (pointer: coarse) {
    .drag-cursor { display: none !important; }
  }

  .below {
    position:fixed;
    bottom:3.5vh; bottom:3.5dvh; left:50%; transform:translateX(-50%);
    z-index:9000; text-align:center; /* see .wall-frame-top note above */
  }
  .below a {
    font-size:14px;
    font-weight:600;
    letter-spacing:0.02em;
    text-transform:uppercase;
    color:var(--color-text-secondary);
    text-decoration:none;
    display:inline-flex;
    align-items:center;
    gap:5px;
    transition:color 0.3s cubic-bezier(0.16,1,0.3,1);
    /* Vertical-only tap-height fix, same reasoning as .wall-mark — the
       parent .below div is itself centered with left:50% +
       translateX(-50%), so horizontal padding here would need the same
       care; vertical-only sidesteps it entirely. */
    padding: 11px 0;
    margin: -11px 0;
  }
  .below a::after {
    content:'↗';
    display:inline-block;
    transition:transform 0.3s cubic-bezier(0.16,1,0.3,1);
  }
  .below a:hover { color:var(--color-text-primary); }
  .below a:hover::after { transform:translate(2px,-2px); }

  .rescatter-btn {
    position:fixed;
    bottom:9vh; bottom:9dvh; left:50%; transform:translateX(-50%);
    z-index:9000; /* see .wall-frame-top note above — this is the actual
       cause of "rescatter doesn't work": a card could sit on top of the
       button and silently eat the click */
    display:flex;
    align-items:center;
    gap:7px;
    background:none;
    border:1px solid var(--color-border);
    border-radius:20px;
    padding:8px 16px 8px 13px;
    /* The pill's own padding put its real tap height around 28-32px on
       desktop and closer to 24px on mobile, under the ~44px a thumb
       needs. min-height keeps the same visual padding and label size
       while giving it a properly sized invisible tap area. */
    min-height:44px;
    font-family:var(--font-primary);
    font-size:12px;
    font-weight:500;
    letter-spacing:0.04em;
    text-transform:uppercase;
    color:var(--color-text-primary);
    cursor:pointer;
    transition:background 0.25s ease, transform 0.15s ease, opacity 0.2s ease;
  }
  .rescatter-btn svg {
    width:13px; height:13px;
    transition:transform 0.5s cubic-bezier(0.65, 0, 0.35, 1);
  }
  .rescatter-btn:hover { background:var(--color-bg-section); }
  .rescatter-btn:hover svg { transform:rotate(180deg); }
  .rescatter-btn:active { transform:translateX(-50%) scale(0.96); }
  .rescatter-btn:disabled { opacity:0.4; cursor:default; }
  .rescatter-btn:focus-visible, .wall-expand-close:focus-visible {
    outline: 2px solid var(--color-text-primary);
    outline-offset: 2px;
  }
  @media (max-width:768px){ .rescatter-btn { bottom:8.8vh; bottom:8.8dvh; padding:7px 13px 7px 11px; font-size:10px; min-height:40px; } }

  /* Sitting at z-index 9000 (needed so cards can never bury it — see the
     note above), it was rendering right over the bottom edge of the
     enlarged sketch while one was open, and could still be clicked from
     there. Hide it for the same span the headline hides — close the
     sketch first, then rescatter. */
  .wall-is-expanded .rescatter-btn {
    opacity: 0;
    pointer-events: none;
  }

  .drag-cursor {
    position:fixed;
    top:0; left:0;
    z-index:10000;
    pointer-events:none;
    background:rgba(255,255,255,0.9);
    color:var(--color-text-secondary);
    font-size:9px;
    font-weight:500;
    letter-spacing:0.06em;
    text-transform:uppercase;
    padding:4px 9px;
    border-radius:20px;
    border:1px solid var(--color-border);
    opacity:0;
    transform:translate(-50%, -50%) scale(0.85);
    transition:opacity 0.2s ease, transform 0.2s ease;
  }
  .drag-cursor.is-visible { opacity:0.85; transform:translate(-50%, -50%) scale(1); }
