@font-face {
  font-family: 'SUIT';
  src: url('fonts/SUIT-Medium.otf') format('opentype');
  font-weight: 500; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'SUIT';
  src: url('fonts/SUIT-Bold.otf') format('opentype');
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'SUIT';
  src: url('fonts/SUIT-ExtraBold.otf') format('opentype');
  font-weight: 800; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'DIN Condensed';
  src: url('fonts/DINCondensed-Bold.ttf') format('truetype');
  font-weight: 700; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'PF Stardust';
  src: url('fonts/PFStardust-Bold.ttf') format('truetype');
  font-weight: 700; font-style: normal; font-display: swap;
}
/* tool-nav row title/desc font (full Hangul coverage — 11172/11172 syllables) */
@font-face {
  font-family: 'Zen Serif';
  src: url('fonts/ZEN-SERIF-Regular.otf') format('opentype');
  font-weight: 400; font-style: normal; font-display: swap;
}

* { box-sizing: border-box; }

/* All text sizes are rem-based; scaling the root scales every text size. */
html { font-size: 150%; }

:root {
  --bg: #000000;
  --surface: #0a0a0a;
  --surface-2: #101012;
  --border: #262626;
  --line-dot: #808080;
  /* crisp evenly-spaced dotted rule (2px dots every 7px) — reads clearly on
     black, unlike a 1px CSS `dotted` border which nearly vanishes. */
  --dotrule: repeating-linear-gradient(to right, var(--line-dot) 0 2px, transparent 2px 7px);
  --text: #ededed;
  --muted: #7d7d80;
  --accent: #a9d3f5; /* unified to the PIN input's sky blue: dot, sliders, active/selection borders, drag highlight */
  --green: #46c46f;
  /* film-credit multicolor accents — pixel-sampled directly from the
     reference screenshot (9 client-credit rows), not eyeballed. */
  --c-red: #ff3131;
  --c-orange: #fd702b;
  --c-yellow: #c6c433;
  --c-green: #00f3af;
  --c-teal: #35c4b0;
  --c-cyan: #6cc4d0;
  --c-navblue: #3395d6; /* the reference's 5th row — distinct from the app's own --c-blue used elsewhere in the editor UI */
  --c-blue: #6b9bff;
  --c-indigo: #8f86ff;
  --c-purple: #c07ce6;
  --c-pink: #9b286b;
  --font: 'SUIT', -apple-system, BlinkMacSystemFont, "Apple SD Gothic Neo", "Pretendard", sans-serif;
  /* scoped to .tool-nav only (playlist.studio-style list rows) — the rest of
     the app stays on --font/SUIT. */
  --font-mono: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
  /* Korean row title/desc in the tool nav (numbers/header stay --font-mono) */
  --font-kr: 'Zen Serif', 'SUIT', serif;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 0.8125rem;
  font-weight: 500;      /* body = SUIT Medium */
  letter-spacing: 0.01em;
  /* NOTE: no -webkit-font-smoothing:antialiased — on macOS it thins strokes and
     makes even ExtraBold headings look light. Default rendering keeps weight. */
}

/* ---- top ruler chrome ---- */
#top-ruler {
  position: fixed; top: 0; left: 0; right: 0; height: 28px;
  z-index: 10000;
  background: #000; border-bottom: 1px solid var(--border);
  display: flex; align-items: stretch;
}
.chrome-icon { width: 18px; height: 18px; align-self: center; margin: 0 10px; object-fit: contain; }
.chrome-ruler { flex: 1; height: 100%; display: block; }
.chrome-clock {
  display: flex; align-items: center; gap: 5px;
  padding: 0 12px; font-size: 0.48rem; letter-spacing: 0.08em; color: var(--muted);
  border-left: 1px solid var(--border); white-space: nowrap;
}
.chrome-live-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--green); box-shadow: 0 0 6px var(--green); }
.chrome-time { color: var(--text); }
.chrome-sep { opacity: 0.6; }
body.has-chrome .app--shell2 { margin-top: 28px; height: calc(100vh - 28px); }

/* custom hammer cursor — same eased/lagging follow + lag-based tilt as
   jjaejae.com's "J" cursor (see js/chrome.js's animate loop), but at the
   original 26px size (not jjaejae.com's 14px). */
body.has-cursor, body.has-cursor * { cursor: none !important; }
#app-cursor {
  position: fixed; top: 0; left: 0;
  width: 34px; height: auto;
  pointer-events: none;
  z-index: 100001;
  transform-origin: center;
  will-change: transform;
  opacity: 0;
  transition: opacity 0.2s ease;
}

/* ---- 2-column shell (nav | content) ---- */
.app--shell2 {
  display: grid;
  grid-template-columns: 22% 6px 78%;
  height: 100vh;
}

.col-resizer { cursor: col-resize; position: relative; }
.col-resizer::after {
  content: '';
  position: absolute;
  top: 0; bottom: 0; left: 2px; right: 2px;
  background: var(--border);
}
.col-resizer:hover::after,
.col-resizer--dragging::after { background: var(--accent); }

.col {
  overflow-y: auto;
  overflow-x: hidden;
  min-height: 0;
  min-width: 0;
  padding: 26px 22px;
}
.col--nav { border-right: 1px solid var(--border); padding-left: 42px; }
/* anchor for .idle-panel's position:absolute (see below) */
.col--content { position: relative; }

.app-title { font-size: 1rem; font-weight: 800; margin: 0 0 8px; letter-spacing: 0.06em; text-transform: uppercase; }

/* wordmark brand logo (replaces the app title text); click returns to home. */
.brand { display: inline-block; margin: 0 0 12px; line-height: 0; }
.brand-logo { height: 34.5px; width: auto; display: block; }
.subtitle {
  color: var(--muted);
  font-size: 0.68rem;
  line-height: 1.6;
  letter-spacing: 0.02em;
  margin: 0 0 22px;
  padding-bottom: 16px;
  background-image: var(--dotrule);
  background-repeat: repeat-x;
  background-size: 100% 2px;
  background-position: left bottom;
}

/* ---- tool nav: playlist.studio-style list rows ----
   Monospace/uppercase list scoped to .tool-nav only (rest of the app stays
   on --font/SUIT). Header row: label + range indicator, thin bottom border.
   Rows bleed edge-to-edge across the column on hover (negative margins
   cancel .col--nav's padding-left:42px / .col's padding-right:22px) so the
   hover fill reads as a full-width bar, not just the padded content width. */
.tool-nav-head {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  margin: 4px 0 0;
  padding-bottom: 10px;
  border-bottom: 1px solid rgba(255,255,255,0.14);
  font-family: var(--font-mono);
  font-size: 0.62rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

.tool-nav { display: flex; flex-direction: column; gap: 0; }
.tool-nav-item {
  display: flex;
  align-items: center;
  gap: 10px;
  text-align: left;
  padding: 12px 22px 12px 42px;
  margin: 0 -22px 0 -42px; /* bleed past .col--nav's padding on both sides */
  border: 0;
  border-bottom: 1px solid rgba(255,255,255,0.18);
  border-radius: 0;
  background-color: transparent; /* <button> defaults to a light ButtonFace bg; keep it black */
  color: var(--text);
  cursor: pointer;
  font-family: var(--font-mono);
  transition: background-color 0.13s ease, color 0.13s ease;
}
/* first-entry stagger: app.js adds --pre to every item before first paint
   (only when landing with no #tool hash), then flips to --in one by one with
   a per-item transition-delay set inline — see revealNav() in app.js.
   Direct/deep-linked loads skip --pre entirely, so nav shows normally. */
.tool-nav-item--pre { opacity: 0; transform: translateY(18px); }
.tool-nav-item--in {
  opacity: 1; transform: translateY(0);
  transition: opacity 0.52s cubic-bezier(0.16, 1, 0.3, 1), transform 0.52s cubic-bezier(0.16, 1, 0.3, 1), background-color 0.13s ease, color 0.13s ease;
}

.tool-nav-index {
  flex: 0 0 auto;
  font-size: 0.62rem;
  letter-spacing: 0.02em;
  color: var(--muted);
  transition: color 0.13s ease;
}

/* leading dot → play-triangle-in-a-circle on hover. Built from two stacked
   pseudo-free spans (no SVG asset needed): the dot is the button's own
   background; the triangle is a CSS border-triangle, hidden until hover. */
.tool-nav-play {
  flex: 0 0 auto;
  width: 17px; height: 17px;
  border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  position: relative;
}
.tool-nav-play::before {
  content: '';
  width: 8px; height: 8px;
  border-radius: 50%;
  background: currentColor;
  transition: opacity 0.12s ease, transform 0.12s ease;
}
/* fixed light-circle + dark-triangle colors (not currentColor) so the badge
   stays legible whether the row's own text is light (default) or has
   flipped dark (hover) — it reads as a "play" affordance, not a text glyph. */
.tool-nav-play-tri {
  position: absolute; inset: 0;
  border-radius: 50%;
  background: #e2e2e2;
  opacity: 0;
  transform: scale(0.4);
  transition: opacity 0.12s ease, transform 0.12s ease;
}
.tool-nav-play-tri::before {
  content: '';
  position: absolute; top: 50%; left: 50%;
  width: 0; height: 0;
  border-style: solid;
  border-width: 4.5px 0 4.5px 7px;
  border-color: transparent transparent transparent #0a0a0a;
  transform: translate(-42%, -50%);
}

/* stacked: title on its own line, desc below it (was a same-line baseline
   row — desc sentences run long enough that title needed the full width). */
.tool-nav-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.tool-nav-label {
  font-family: var(--font-kr);
  font-size: 0.697rem; /* 0.86rem - 10% - 10% */
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tool-nav-desc {
  font-family: var(--font-kr);
  min-width: 0;
  font-size: 0.535rem; /* 0.66rem - 10% - 10% */
  letter-spacing: 0.01em;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: color 0.13s ease;
}

/* ---- hover: full-bleed light fill, dark text, dot → play triangle ---- */
.tool-nav-item:hover {
  background-color: #e2e2e2;
  color: #0a0a0a;
}
.tool-nav-item:hover .tool-nav-index,
.tool-nav-item:hover .tool-nav-desc { color: rgba(10,10,10,0.62); }
.tool-nav-item:hover .tool-nav-play::before { opacity: 0; transform: scale(0.4); }
.tool-nav-item:hover .tool-nav-play-tri { opacity: 1; transform: scale(1); }

/* persistent active-tool indicator: left accent bar + filled play icon,
   coherent with the new monochrome/monospace look (replaces the old
   underline + "▸ " prefix combo). */
.tool-nav-item--active { box-shadow: inset 3px 0 0 var(--accent); }
.tool-nav-item--active .tool-nav-play::before { background: var(--accent); }
.tool-nav-item--active:hover .tool-nav-play::before { background: #0a0a0a; }

.tool-loading, .tool-error { color: var(--muted); padding: 40px 4px; letter-spacing: 0.04em; }
.tool-error p { margin: 0 0 6px; }

/* Font Maker (and any full-page tool) reads as a link */
.tool-nav-item--link .tool-nav-label::after { content: ' ↗'; color: var(--muted); }
.tool-nav-item--link:hover .tool-nav-label::after { color: rgba(10,10,10,0.62); }

/* entry password gate */
.gate-overlay {
  position: fixed; inset: 0;
  background: var(--bg);
  z-index: 9999;
  display: flex; flex-direction: column; align-items: center;
  overflow-y: auto; /* safety net: if wrapped content is ever taller than the
    viewport (e.g. a wide-but-short window), scroll instead of clipping the
    top off-screen — this is what broke Safari's shorter window. */
  padding: 24px 0;
}
/* `align-items:center` alone clips/strands the overflowing top of the content
   on short viewports (classic flexbox-centering bug: the browser won't let
   scrollTop go negative to reach whatever sits above the centered line, so
   the top portion — WELCOME TO + wordmark — became both invisible AND
   unreachable by scrolling on mobile). Auto-margin spacers center the row
   only while it fits, and collapse to 0 (never negative) once it overflows,
   so the full content always starts flush at the scrollable top. */
.gate-overlay::before,
.gate-overlay::after {
  content: '';
  margin: auto 0;
}
/* three-up row: wordmark | looping coin video | code-entry card. Stacks
   vertically on narrow/mobile viewports (see the media query below).
   flex-wrap is a safety net: if the row is ever wider than the viewport with
   the (intentionally non-shrinking) video panel + card, items wrap to a new
   line instead of overlapping. */
.gate-row { display: flex; flex-wrap: wrap; align-items: center; justify-content: center; gap: 80px; max-width: 94vw; }
.gate-title { font-size: 1rem; font-weight: 800; margin-bottom: 8px; letter-spacing: 0.1em; text-transform: uppercase; }
.gate-left { display: flex; flex-direction: column; align-items: center; }
/* Sized so "WELCOME TO" reads clearly smaller than the wordmark below it
   (~2.8:1 width ratio in the reference) — 2rem made it render almost as wide
   as the 300px logo, which is why the two looked like a similar-weight pair
   instead of a small label + big logo. */
.gate-welcome {
  font-family: 'DIN Condensed', 'SUIT', sans-serif;
  font-size: 0.7rem; font-weight: 700; letter-spacing: 0.14em;
  color: var(--text); margin-bottom: 6px; text-transform: uppercase;
}
.gate-logo { width: 280px; max-width: 44vw; height: auto; display: block; }

/* No background/border — the video's own black corners blend straight into
   the page background, so only the coin itself reads on screen (no visible
   "box" around it). Sized large so the coin animation feels big/immersive. */
.gate-video-panel {
  position: relative; flex-shrink: 0;
  width: clamp(748px, min(71.5vw, 67vh), 1341px); aspect-ratio: 1; /* +10% again; still capped by viewport HEIGHT so Safari's shorter window doesn't force a wrap */
  background: transparent; border: none; border-radius: 0;
  overflow: hidden;
}
.gate-coin-video { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Simple flat pill, fixed to the page's own bottom-right corner (not the
   video panel) — label = the action a click takes, per convention.
   Sized 20% down from the original 9px/18px padding + 0.68rem font. */
.gate-sound-btn {
  position: fixed; right: 22px; bottom: 20px; z-index: 10000;
  padding: 7px 14px; border-radius: 999px;
  background: transparent; border: 1px solid #4a4a4d;
  color: var(--text); font-family: inherit;
  font-size: 0.544rem; font-weight: 700; letter-spacing: 0.08em;
  cursor: pointer;
}
.gate-sound-btn:hover { border-color: #fff; }

.gate-card {
  width: 320px; max-width: 76vw; flex-shrink: 0;
  background: #151516; border: 1px solid #262629; border-radius: 28px;
  padding: 24px 20px 20px;
  display: flex; flex-direction: column; align-items: center; gap: 16px;
}
.gate-sphere { width: 180px; height: 180px; overflow: hidden; }

@media (max-width: 860px) {
  /* extra top padding so the now-reachable WELCOME TO/wordmark doesn't sit
     flush under the fixed 28px top ruler chrome. */
  .gate-overlay { padding: 44px 0 24px; }
  .gate-row { flex-direction: column; gap: 28px; }
  .gate-left { align-items: center; text-align: center; }
  .gate-logo { max-width: 78vw; }
  .gate-video-panel { width: min(340px, 78vw); aspect-ratio: 1; }
  .gate-card { width: min(340px, 78vw); max-width: none; }
  /* smaller + moved to the top-right (below the fixed top ruler) so it never
     sits over the PIN cells, which stacking pushes toward the page bottom. */
  .gate-sound-btn {
    padding: 5px 10px;
    font-size: 0.46rem;
    top: 36px; bottom: auto;
    right: 14px;
  }
}
.gate-card-label {
  font-family: 'DIN Condensed', 'SUIT', sans-serif;
  font-size: 1.2rem; font-weight: 700; letter-spacing: 0.1em;
  color: #f0f0f0; text-transform: uppercase;
}
/* row of 4 pill inputs — no separate backdrop box; the ovals sit directly on
   the card's own background so only the card color is visible around them,
   exactly like the "ENTER THE CODE" label above it. */
.gate-pin { display: flex; gap: 8px; width: 100%; }
/* higher specificity (.gate-pin .pin-box) so it beats the generic
   input[type="text"] rule; a tall pill (border-radius:999px) = 완전 세로 라운드형 */
/* Each cell = an invisible <input> (captures typing/caret) stacked under a
   flex-centered <span> that actually displays the digit. Centering the digit
   via flexbox is exact regardless of font metrics — line-height-based
   centering alone left DIN Condensed's numerals reading "too high" because
   the font reserves far less descender space than ascender space. */
.pin-cell {
  position: relative; flex: 1; min-width: 0; /* flex items default to
    min-width:auto, which uses the <input>'s intrinsic content width (~20
    chars) and blocks shrinking below that — this pushed the pills out past
    the card before. */
  height: 78px; border-radius: 999px;
  background: transparent; border: 1.5px solid #3a3a3d;
  transition: background-color 0.15s ease, border-color 0.15s ease;
}
.pin-cell:focus-within { border-color: #fff; }
.pin-cell--filled { background: #a9d3f5; border-color: #a9d3f5; } /* sky blue */
.pin-display {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  font-family: 'DIN Condensed', 'SUIT', sans-serif;
  font-size: 2rem; font-weight: 700; line-height: 1; color: var(--text);
  pointer-events: none;
}
/* Flex-centering aligns the SPAN's own box, but DIN Condensed's numerals sit
   high within that box (its line-height reserves far more room above the
   glyph than below) — nudge the glyph itself down to compensate. */
.pin-display { padding-top: 0.22em; }
.pin-cell--filled .pin-display { color: #10202e; }
.gate-pin .pin-box {
  position: absolute; inset: 0; width: 100%; height: 100%;
  background: transparent; border: none; color: transparent;
  text-align: center; font-size: 2rem; font-weight: 700; outline: none;
  font-family: 'DIN Condensed', 'SUIT', sans-serif;
  caret-color: var(--text); padding: 0;
}
@keyframes gateShake { 0%,100%{transform:translateX(0)} 20%{transform:translateX(-7px)} 60%{transform:translateX(7px)} }
.gate-card.shake { animation: gateShake 0.35s; }
.gate-error { color: var(--c-red); font-size: 0.8rem; margin: 0; letter-spacing: 0.04em; }

/* ---- shared tool UI (sizes match Codex's documented pass: 22.8→19.2px
   title, 17.3→14.9px section label, body/settings/buttons to ~13-15px) ---- */
.tool-head { margin-bottom: 18px; }
.tool-head h2 { font-size: 0.8rem; font-weight: 800; margin: 0 0 6px; letter-spacing: 0.12em; text-transform: uppercase; }
.tool-head p { color: var(--muted); font-size: 0.6rem; margin: 0; line-height: 1.6; letter-spacing: 0.01em; }

/* two-column "canvas + settings" shell — see toolShell() in canvasUtils.js.
   .tool-panel sticks to the top of .col--content's own scrollport (the
   ancestor that actually scrolls) so settings stay in view without a
   separate scroll container of their own; it only grows its own scrollbar
   if its content is taller than the viewport. */
.tool-shell { display: flex; align-items: flex-start; gap: 28px; }
.tool-main { flex: 1 1 auto; min-width: 0; }
.tool-panel {
  width: 300px; flex-shrink: 0;
  position: sticky; top: 0;
  max-height: 100vh;
  overflow-y: auto;
  padding-bottom: 26px; /* mirrors .col's own bottom padding once sticky clips it off */
}
@media (max-width: 860px) {
  .tool-shell { flex-direction: column; align-items: stretch; gap: 0; }
  .tool-panel { position: static; width: 100%; max-height: none; overflow-y: visible; }
}

.block {
  padding: 18px;
  margin-bottom: 16px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--surface-2);
  transition: background-color 0.15s ease, border-color 0.15s ease;
  outline: 1px dashed transparent;
  outline-offset: 2px;
}
.block.drag-over { outline-color: var(--accent); border-color: var(--accent); background-color: rgba(169, 211, 245, 0.08); }
.block-label {
  display: flex; align-items: center; gap: 8px;
  color: var(--text);
  font-weight: 800; font-size: 0.62rem;
  letter-spacing: 0.1em; text-transform: uppercase;
  margin-bottom: 16px;
}
.dot { width: 6px; height: 6px; border-radius: 2px; background: var(--accent); display: inline-block; flex-shrink: 0; }
.block .ctl-row:last-child { margin-bottom: 0; }

/* dropzone / empty state */
.dropzone {
  border: 1px dashed var(--border);
  border-radius: 8px;
  padding: 26px 18px;
  text-align: center;
  color: var(--muted);
  cursor: pointer;
  font-size: 0.58rem;
  letter-spacing: 0.03em;
  transition: border-color 0.15s ease, background-color 0.15s ease;
}
.dropzone:hover { border-color: var(--text); background: var(--surface); color: var(--text); }
.dropzone strong { display: block; color: var(--text); font-size: 0.62rem; letter-spacing: 0.05em; text-transform: uppercase; margin-bottom: 6px; }

/* buttons & inputs — flat, square, uppercase */
.pill-btn {
  padding: 8px 16px;
  border-radius: 7px;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text);
  cursor: pointer;
  font-family: inherit;
  font-weight: 700;
  font-size: 0.58rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  transition: background-color 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.pill-btn:hover { background: var(--text); color: #000; border-color: var(--text); }
.pill-btn--sm { padding: 5px 11px; font-size: 0.54rem; }
.pill-btn:disabled { opacity: 0.35; cursor: not-allowed; }
.pill-btn:disabled:hover { background: transparent; color: var(--text); border-color: var(--border); }

.pill-btn--primary { background: var(--text); color: #000; border-color: var(--text); font-weight: 700; }
.pill-btn--primary:hover { background: #fff; color: #000; }

input[type="text"], input[type="number"], input[type="password"] {
  padding: 8px 12px;
  border-radius: 7px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  font-family: inherit;
  font-size: 0.6rem;
  outline: none;
}
input[type="text"]:focus, input[type="number"]:focus, input[type="password"]:focus { border-color: var(--text); }
input[type="range"] { accent-color: var(--accent); }
input[type="color"] { border: 1px solid var(--border); border-radius: 6px; background: var(--surface); cursor: pointer; }

select {
  padding: 7px 11px;
  border-radius: 7px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  font-family: inherit;
  font-size: 0.58rem;
  letter-spacing: 0.03em;
  outline: none;
  cursor: pointer;
}

/* inspector-style control rows */
.ctl-row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  flex-wrap: wrap; width: 100%;
  margin-bottom: 14px;
  font-size: 0.58rem;
  letter-spacing: 0.02em;
  color: var(--muted);
}
.ctl-row label { display: flex; align-items: center; gap: 8px; }
.ctl-row > label:only-child { width: 100%; justify-content: space-between; }
.ctl-row-label { color: var(--text); min-width: 0; }
.ctl-row-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; width: 100%; }
.ctl-row--range { display: flex; flex-direction: column; align-items: stretch; gap: 9px; }
.ctl-row--disabled { opacity: 0.4; pointer-events: none; }
/* a .ctl-row can itself be a drop target (e.g. stamp filter's texture-image
   upload) — same highlight .block.drag-over uses elsewhere, since .ctl-row
   isn't a .block and wouldn't otherwise get any visual feedback on drag */
.ctl-row.drag-over { outline: 1px dashed var(--accent); outline-offset: 4px; background-color: rgba(169, 211, 245, 0.08); }
.ctl-value { color: var(--text); min-width: 40px; text-align: right; }
.ctl-value-pill {
  min-width: 66px; height: 28px;
  display: inline-flex; align-items: center; justify-content: flex-end; gap: 4px;
  padding: 0 9px;
  border: 1px solid var(--border); border-radius: 8px;
  background: var(--surface); color: var(--text);
  transition: border-color 0.12s ease, background-color 0.12s ease;
}
.ctl-value-pill:focus-within { border-color: var(--text); }
.ctl-value-pill .ctl-value-input {
  width: 44px; min-width: 0; height: auto; flex: 1 1 auto;
  background: transparent; border: 0; border-radius: 0;
  color: var(--text); font-family: inherit; font-size: 0.58rem;
  text-align: right; padding: 0; outline: none;
}
.ctl-value-input { -moz-appearance: textfield; appearance: textfield; }
.ctl-value-input::-webkit-inner-spin-button,
.ctl-value-input::-webkit-outer-spin-button { -webkit-appearance: none; appearance: none; margin: 0; }
.ctl-unit { color: var(--muted); flex-shrink: 0; }

/* The fill is driven by rangeRow's --pct custom property, keeping the
   native input semantics while replacing the OS-specific track rendering. */
.ctl-row .ctl-range {
  --pct: 0%;
  width: 100%; min-width: 0; height: 16px; margin: 0;
  appearance: none; -webkit-appearance: none;
  border: 0; border-radius: 0; outline: none; cursor: pointer;
  background: linear-gradient(to right,
    var(--text) 0 var(--pct),
    var(--border) var(--pct) 100%) center / 100% 3px no-repeat;
}
.ctl-row .ctl-range::-webkit-slider-runnable-track { height: 3px; background: transparent; border: 0; }
.ctl-row .ctl-range::-webkit-slider-thumb {
  width: 12px; height: 12px; margin-top: -4.5px;
  appearance: none; -webkit-appearance: none;
  border: 2px solid var(--surface-2); border-radius: 50%;
  background: var(--text);
  box-shadow: 0 0 0 1px rgba(255,255,255,0.3), 0 2px 5px rgba(0,0,0,0.55);
}
.ctl-row .ctl-range::-moz-range-track { height: 3px; background: transparent; border: 0; }
.ctl-row .ctl-range::-moz-range-progress { height: 3px; background: transparent; }
.ctl-row .ctl-range::-moz-range-thumb {
  width: 10px; height: 10px;
  border: 2px solid var(--surface-2); border-radius: 50%;
  background: var(--text);
  box-shadow: 0 0 0 1px rgba(255,255,255,0.3), 0 2px 5px rgba(0,0,0,0.55);
}
.ctl-row .ctl-range:focus-visible { box-shadow: 0 0 0 2px var(--accent); border-radius: 4px; }

/* Color swatch + editable hex pill. */
.ctl-color-control { display: inline-flex; align-items: center; gap: 6px; }
.ctl-color-control .ctl-color-swatch {
  appearance: none; -webkit-appearance: none;
  width: 22px; height: 22px; padding: 0;
  border: 1px solid rgba(255,255,255,0.24); border-radius: 5px;
  background: transparent; overflow: hidden; cursor: pointer;
}
.ctl-color-swatch::-webkit-color-swatch-wrapper { padding: 0; }
.ctl-color-swatch::-webkit-color-swatch { border: 0; border-radius: 4px; }
.ctl-color-swatch::-moz-color-swatch { border: 0; border-radius: 4px; }
.ctl-color-control .ctl-color-hex {
  width: 78px; height: 28px; padding: 0 10px;
  border: 1px solid var(--border); border-radius: 8px;
  background: var(--surface); color: var(--text);
  font-family: var(--font-mono); font-size: 0.56rem;
  text-align: center; letter-spacing: 0.04em; text-transform: uppercase;
}
.ctl-color-control .ctl-color-hex:focus { border-color: var(--text); }

/* Free-text caption fields (postcard filter's title/No./keywords/date) —
   label stacked above a full-width input, unlike colorRow's side-by-side
   layout, since these values run longer than a hex code. */
.ctl-row--text { flex-direction: column; align-items: stretch; gap: 6px; }
.ctl-text-input {
  width: 100%; height: 32px; padding: 0 10px;
  border: 1px solid var(--border); border-radius: 8px;
  background: var(--surface); color: var(--text);
  font-family: inherit; font-size: 0.62rem;
}
.ctl-text-input:focus { border-color: var(--text); outline: none; }
.ctl-text-input::placeholder { color: var(--muted); }

/* Shared segmented control (Yes/No, brush modes, and export formats). */
.ctl-toggle {
  display: inline-flex; align-items: center;
  padding: 2px; border: 1px solid var(--border); border-radius: 9px;
  background: var(--surface); overflow: hidden;
}
.ctl-toggle-option {
  min-width: 38px; height: 24px; padding: 0 10px;
  border: 0; border-radius: 7px; background: transparent;
  color: var(--muted); cursor: pointer;
  font: inherit; font-size: 0.54rem; letter-spacing: 0.02em;
  transition: background-color 0.12s ease, color 0.12s ease;
}
.ctl-toggle-option + .ctl-toggle-option { box-shadow: -1px 0 0 var(--border); }
.ctl-toggle-option--active { background: var(--border); color: var(--text); box-shadow: none; }
.ctl-toggle-option:focus-visible { outline: 1px solid var(--accent); outline-offset: -1px; }
.ctl-export-control { display: inline-flex; align-items: center; gap: 7px; }
.ctl-row--export { margin-top: 12px; }
.ctl-export-button {
  width: 30px; height: 30px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 9px; font-size: 0.72rem; line-height: 1;
}
.ctl-export-button svg { width: 16px; height: 16px; display: block; }

.btn-row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }

/* thumbnails / previews */
.thumb-grid { display: flex; flex-wrap: wrap; gap: 10px; }
.thumb {
  width: 120px; height: 120px;
  border-radius: 0; overflow: hidden; position: relative;
  background: var(--surface); border: 1px solid var(--border);
}
.thumb img, .thumb canvas { width: 100%; height: 100%; object-fit: cover; display: block; }
.thumb-remove {
  position: absolute; top: 4px; right: 4px;
  width: 20px; height: 20px; border-radius: 5px; border: 1px solid rgba(255,255,255,0.3);
  background: rgba(0,0,0,0.7); color: #fff; font-size: 13px; line-height: 1; cursor: pointer;
}
.thumb-trim {
  position: absolute; left: 4px; bottom: 4px;
  padding: 3px 8px; border-radius: 5px; border: 1px solid rgba(255,255,255,0.35);
  background: rgba(0,0,0,0.7); color: #fff; font-size: 0.58rem; letter-spacing: 0.06em;
  text-transform: uppercase; cursor: pointer;
}
.thumb-trim:hover { background: #fff; color: #000; }

.candidate-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 6px; }

/* crop / trim modal */
.crop-modal {
  position: fixed; inset: 0; z-index: 9998;
  background: rgba(0,0,0,0.82);
  display: flex; align-items: center; justify-content: center;
}
.crop-modal-box {
  background: var(--surface); border: 1px solid var(--border);
  padding: 18px; max-width: 92vw; max-height: 92vh; overflow: auto;
}
.crop-modal-title {
  font-weight: 800; font-size: 0.8rem; letter-spacing: 0.12em; text-transform: uppercase;
  margin-bottom: 14px;
}
.crop-stage { position: relative; margin: 0 auto; user-select: none; touch-action: none; }
.crop-stage canvas { display: block; width: 100%; height: 100%; }
.crop-box {
  position: absolute; box-sizing: border-box;
  border: 1px solid #fff;
  box-shadow: 0 0 0 9999px rgba(0,0,0,0.5);
  cursor: move;
}
.crop-handle {
  position: absolute; width: 12px; height: 12px;
  background: #a9d3f5; border: 1px solid #000;
}
.crop-handle--nw { left: -6px; top: -6px; cursor: nwse-resize; }
.crop-handle--ne { right: -6px; top: -6px; cursor: nesw-resize; }
.crop-handle--sw { left: -6px; bottom: -6px; cursor: nesw-resize; }
.crop-handle--se { right: -6px; bottom: -6px; cursor: nwse-resize; }

/* interactive layout editor */
.editor-stage-wrap {
  margin-top: 4px;
  max-height: calc(60vh + 2px);
  overflow: auto;
}
.editor-stage {
  position: relative;
  margin: 0 auto;
  background: repeating-conic-gradient(#141414 0% 25%, #0b0b0b 0% 50%) 50% / 20px 20px;
  border: 1px solid var(--border);
  overflow: hidden;
  touch-action: none;
}
/* Positioning shell (left/top/width/height/rotation/z-index, set inline per
   cell) — deliberately NOT clipped, so handles/toolbar positioned straddling
   or above its edge (children of THIS element, not .editor-tile) render in
   full instead of being cut off by .editor-tile's own overflow:hidden. */
.editor-tile-wrap { position: absolute; }
.editor-tile-wrap--selected { z-index: 999 !important; }
.editor-tile { position: absolute; inset: 0; width: 100%; height: 100%; cursor: move; overflow: hidden; }
.editor-tile canvas, .editor-tile img { width: 100%; height: 100%; display: block; }
.editor-tile--selected { outline: 2px solid #a9d3f5; outline-offset: 0; }
.editor-tile--adjust { outline: 2px dashed var(--c-yellow); cursor: grab; }
.editor-tile--adjust:active { cursor: grabbing; }
.editor-tile--drop { outline: 3px solid var(--c-green); outline-offset: -2px; }

/* alignment guide lines (Figma-style smart guides) — shown only while a
   drag/resize is actually snapped to something, via the 🧲 자석 toggle */
.editor-guide { position: absolute; background: #ff3b6b; pointer-events: none; z-index: 1000; }
.editor-guide--v { top: 0; bottom: 0; width: 1px; margin-left: -0.5px; }
.editor-guide--h { left: 0; right: 0; height: 1px; margin-top: -0.5px; }
.editor-tile--drop::after {
  content: '↔ 위치 교체'; position: absolute; left: 50%; top: 50%; transform: translate(-50%,-50%);
  background: var(--c-green); color: #000; font-size: 0.62rem; font-weight: 700;
  padding: 3px 8px; white-space: nowrap; pointer-events: none;
}
.editor-handle {
  position: absolute; width: 11px; height: 11px;
  background: #fff; border: 1px solid #333; box-sizing: border-box; z-index: 5;
}
.editor-tile--image { overflow: visible; }
.editor-imgfull { position: absolute; z-index: 0; }
.editor-framemask {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.55);
  border: 1px solid rgba(255, 255, 255, 0.9);
}

/* floating Keynote-style edit bar */
.editor-editbar {
  position: fixed; left: 50%; bottom: 26px; transform: translateX(-50%);
  z-index: 9990;
  display: flex; align-items: center; gap: 10px;
  background: #101012; border: 1px solid var(--border);
  padding: 8px 12px; box-shadow: 0 8px 30px rgba(0,0,0,0.5);
}
.editbar-tab {
  font-family: inherit; font-size: 0.66rem; letter-spacing: 0.03em;
  padding: 5px 10px; border: 1px solid var(--border); background: transparent; color: var(--text); cursor: pointer; border-radius: 6px;
}
.editbar-tab--active { background: #a9d3f5; color: #000; border-color: #a9d3f5; font-weight: 700; }
.editbar-gauge { width: 160px; accent-color: #a9d3f5; }
/* exact canvas-pixel X/Y/W/H fields, frame mode only */
.editbar-dims { display: flex; gap: 8px; }
.editbar-dim-field {
  display: flex; align-items: center; gap: 4px;
  font-size: 0.6rem; color: var(--muted); letter-spacing: 0.03em;
}
.editbar-dim {
  width: 54px; font-family: inherit; font-size: 0.66rem;
  padding: 4px 6px; border: 1px solid var(--border); border-radius: 5px;
  background: #000; color: var(--text);
}
/* right-click layer-order menu on an editor tile */
.editor-ctxmenu {
  position: fixed; z-index: 9995;
  display: flex; flex-direction: column;
  background: #101012; border: 1px solid var(--border);
  box-shadow: 0 8px 30px rgba(0,0,0,0.5); min-width: 150px; padding: 4px;
}
.editor-ctxmenu button {
  font-family: inherit; font-size: 0.68rem; text-align: left;
  padding: 7px 10px; border: none; background: transparent; color: var(--text); cursor: pointer; border-radius: 4px;
}
.editor-ctxmenu button:hover { background: #a9d3f5; color: #000; }
/* image-content rotate buttons shown directly on the tile in image-edit mode.
   Sits INSIDE the tile's own top-right corner (not above it, unlike
   .editor-toolbar) — a tile near the viewport top would otherwise push an
   above-frame control under the app's fixed top ruler chrome, making it
   unreachable regardless of scroll position. */
.editor-imgrotate-bar {
  position: absolute; top: 6px; right: 6px;
  display: flex; align-items: center; gap: 6px; z-index: 3;
  background: rgba(0,0,0,0.55); padding: 4px 6px; border-radius: 6px;
}
.editor-imgrotate-bar button {
  padding: 3px 8px; font-size: 0.68rem; border: 1px solid rgba(255,255,255,0.3);
  background: rgba(0,0,0,0.75); color: var(--text); cursor: pointer; font-family: inherit; border-radius: 5px;
}
.editor-imgrotate-slider { width: 90px; accent-color: #a9d3f5; }
.editor-imgrotate-readout {
  font-size: 0.6rem; color: var(--text); min-width: 30px; text-align: center;
  font-variant-numeric: tabular-nums;
}
.editor-imgrotate-bar button:hover { background: var(--text); color: #000; }
/* Sits INSIDE the tile's own top-left corner (not above it) — same reasoning
   as .editor-imgrotate-bar above: now that handles/toolbar live in the
   unclipped .editor-tile-wrap instead of the clipped .editor-tile, an
   above-frame position for a top-row tile can collide with the instruction
   text above the stage (confirmed via Playwright: the hint paragraph
   intercepted clicks meant for these buttons). Never simultaneously visible
   with .editor-imgrotate-bar (different modes), so top-left vs its top-right
   is just for visual distinction, not to avoid an actual collision. */
.editor-toolbar {
  position: absolute; top: 6px; left: 6px;
  display: flex; gap: 4px; z-index: 3;
  background: rgba(0,0,0,0.55); padding: 4px 6px; border-radius: 6px;
}
.editor-toolbar button {
  padding: 3px 7px; font-size: 0.6rem; border: 1px solid rgba(255,255,255,0.3);
  background: rgba(0,0,0,0.75); color: var(--text); cursor: pointer; font-family: inherit; border-radius: 5px;
}
.editor-toolbar button:hover { background: var(--text); color: #000; }
.editor-toolbar-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 12px; }
.editor-export-group,
.editor-zoom-row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  margin-top: 12px; padding: 10px 12px;
  border: 1px solid var(--border); border-radius: 10px;
  background: var(--surface);
}
.editor-tool-group-label {
  color: var(--text); font-size: 0.58rem; font-weight: 700;
  letter-spacing: 0.04em;
}
.editor-zoom-controls { display: inline-flex; align-items: center; gap: 6px; }
.editor-zoom-button { width: 28px; padding: 0; font-size: 0.75rem; }
.editor-zoom-readout {
  min-width: 48px; height: 26px; padding: 0 8px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--border); border-radius: 8px;
  background: var(--surface-2); color: var(--text);
  font-family: var(--font-mono); font-size: 0.55rem;
  font-variant-numeric: tabular-nums;
}

.preview-stage {
  border: 1px solid var(--border);
  border-radius: 0;
  background: repeating-conic-gradient(#141414 0% 25%, #0b0b0b 0% 50%) 50% / 20px 20px;
  padding: 12px;
  display: flex; align-items: center; justify-content: center;
  min-height: 200px;
  max-height: 60vh;
  overflow: hidden;
}
.preview-stage canvas, .preview-stage img {
  max-width: 100%; max-height: calc(60vh - 24px);
  width: auto; height: auto;
  object-fit: contain; display: block; border-radius: 0;
}
.preview-stage .orig-backdrop,
.preview-stage .mask-overlay {
  width: 100%; height: 100%;
  max-width: none; max-height: none;
}
.preview-wrap { position: relative; width: 100%; }
.preview-expand-btn {
  position: absolute; top: 10px; right: 10px; z-index: 10;
  width: 30px; height: 30px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid rgba(255,255,255,0.3); border-radius: 8px;
  background: rgba(0,0,0,0.72); color: var(--text);
  font-family: inherit; font-size: 0.68rem; cursor: pointer;
  box-shadow: 0 3px 10px rgba(0,0,0,0.35);
}
.preview-expand-btn:hover { background: var(--text); color: var(--bg); border-color: var(--text); }
.preview-expand-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.preview-lightbox-box {
  position: relative; padding: 46px 16px 16px;
  max-width: 94vw; max-height: 94vh;
  border-radius: 14px;
  background: repeating-conic-gradient(#141414 0% 25%, #0b0b0b 0% 50%) 50% / 20px 20px;
}
.preview-lightbox-close {
  position: absolute; top: 10px; right: 10px; z-index: 2;
  width: 28px; height: 28px; padding: 0;
  border: 1px solid var(--border); border-radius: 8px;
  background: var(--surface); color: var(--text);
  font-family: inherit; font-size: 0.8rem; cursor: pointer;
}
.preview-lightbox-close:hover { background: var(--text); color: var(--bg); }
.preview-lightbox-media { display: block; max-width: none; max-height: none; width: auto; height: auto; }

/* 배경 제거 brush touch-up — the editable result canvas hides the native
   cursor (a custom circle overlay shows brush size instead) and blocks touch
   scrolling so a stylus/finger drag paints instead of scrolling the page. */
.brush-canvas { cursor: none; touch-action: none; }
.brush-cursor {
  position: absolute; display: none; z-index: 5; pointer-events: none;
  border: 1.5px solid #fff; border-radius: 50%;
  box-shadow: 0 0 0 1.5px rgba(0,0,0,0.6);
  transform: translate(0, 0);
}
.brush-cursor--restore { border-color: var(--c-green, #4caf50); }

/* pending 지우기/복구 marks — visual-only overlays, sit above the result
   canvas but never receive pointer events (the canvas underneath does) */
.mask-overlay { position: absolute; left: 0; top: 0; width: 100%; height: 100%; pointer-events: none; z-index: 2; }

/* bento grid placeholder Bg cycle — a row of small swatch color inputs */
.swatch-cycle { display: flex; gap: 6px; flex-wrap: wrap; }
.swatch-cycle input[type="color"] { width: 26px; height: 26px; padding: 0; border-radius: 4px; }

/* 3x3 directional-bleed control on the stamp filter — center/N/S/E/W/corners */
.bleed-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; width: 168px; margin-top: 4px; }
.bleed-grid-cell { display: flex; flex-direction: column; align-items: center; gap: 2px; }
.bleed-grid-label { font-size: 0.52rem; color: var(--muted); }
.bleed-grid-input {
  width: 100%; box-sizing: border-box; text-align: center; padding: 4px 2px;
  background: transparent; border: 1px solid #3a3a3d; border-radius: 4px;
  color: var(--text); font-family: inherit; font-size: 0.6rem;
  -moz-appearance: textfield; appearance: textfield;
}
.bleed-grid-input:focus { outline: none; border-color: var(--accent); }
.bleed-grid-input::-webkit-inner-spin-button,
.bleed-grid-input::-webkit-outer-spin-button { -webkit-appearance: none; appearance: none; margin: 0; }

/* dimmed original photo behind the result canvas — lets transparent/erased
   areas show a faint hint of what's actually there, so the brush can be
   aimed accurately instead of guessing at a checkerboard */
.orig-backdrop { position: absolute; left: 0; top: 0; width: 100%; height: 100%; pointer-events: none; z-index: 0; filter: grayscale(55%) brightness(0.55); opacity: 0.85; }

/* Magic Brush toggle switch */
.switch { position: relative; display: inline-block; width: 34px; height: 18px; vertical-align: middle; margin-right: 8px; }
.switch input { position: absolute; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
.switch-track { position: absolute; inset: 0; background: #333; border: 1px solid var(--border); border-radius: 999px; transition: background 0.15s ease; pointer-events: none; }
.switch-track::before {
  content: ''; position: absolute; left: 2px; top: 1px; width: 14px; height: 14px;
  background: #fff; border-radius: 50%; transition: transform 0.15s ease;
}
.switch input:checked + .switch-track { background: #a9d3f5; }
.switch input:checked + .switch-track::before { transform: translateX(16px); }

/* layout candidates */
.candidate-wrap { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 14px; }
.candidate { border: 1px solid var(--border); border-radius: 0; overflow: hidden; background: var(--surface); }
.candidate-canvas { display: block; width: 100%; height: auto; background: #fff; }
.candidate-bar {
  display: flex; align-items: center; justify-content: space-between;
  padding: 8px 12px; font-size: 0.72rem; letter-spacing: 0.03em; gap: 8px;
  background-image: var(--dotrule);
  background-repeat: repeat-x;
  background-size: 100% 2px;
  background-position: left top;
}

/* 9-point anchor picker */
.anchor-grid { display: grid; grid-template-columns: repeat(3, 26px); grid-template-rows: repeat(3, 26px); gap: 4px; }
.anchor-cell { border: 1px solid var(--border); background: var(--surface); border-radius: 5px; cursor: pointer; padding: 0; }
.anchor-cell:hover { background: var(--surface-2); }
.anchor-cell--active { background: var(--accent); border-color: var(--accent); }

/* palette swatches */
.swatch-wrap { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
.swatch {
  width: 96px; height: 96px;
  border-radius: 7px; border: 1px solid var(--border);
  cursor: pointer; position: relative; padding: 0;
  display: flex; align-items: flex-end; justify-content: center;
}
.swatch:hover { transform: translateY(-2px); }
.swatch-hex {
  font-size: 10px; font-weight: 600; letter-spacing: 0.04em;
  padding: 3px 6px; margin-bottom: 6px; border-radius: 0;
  background: rgba(0,0,0,0.55);
}

/* fixed brand mark, bottom-left of the viewport. Hidden while idle (no
   "hero"/centered phase anymore — the right panel's own idle video, below,
   owns that moment); simply fades in in-place the instant the first tool is
   picked (see leaveIdle() in app.js). Sizing while non-idle is handed off to
   sizeCornerLogo() (app.js), which sets left/top/height inline relative to
   #colNav's width — the bare CSS left/bottom here is only the pre-JS resting
   spot (and the mobile media query's fallback numbers). */
.logo-mark {
  position: fixed; left: 24px; bottom: 24px;
  height: 260px; width: auto;
  opacity: 0; pointer-events: none; z-index: 50;
  transition: opacity 0.5s ease;
  /* MP4/H.264 has no alpha channel, so the video can't actually carry
     transparency — but its background is genuinely black (confirmed by
     pixel-sampling the source frames), and `screen` blending makes true
     black pixels contribute nothing to whatever sits behind them while
     leaving the lighter line-art/flame colors untouched. On this app's
     all-black backdrop that reads as a fully transparent cutout with zero
     re-encoding needed. */
  mix-blend-mode: screen;
}
.logo-mark--shown { opacity: 1; }
/* 260px is sized for wide desktop windows; on phone widths that's most of
   the screen and covers real UI underneath it (this rule must come after
   the base .logo-mark rule above — same selector specificity, so source
   order decides the winner regardless of the media query matching). */
@media (max-width: 860px) {
  .logo-mark { height: 90px; left: 14px; bottom: 14px; }
}

/* ---- idle "big media" panel (right content column) ----
   Shown in place of #toolHost until a tool is picked (app.js's `idle` flag).
   Positioned in-flow inside #colContent (which .col already makes a
   scrollport with padding) so it naturally sizes with the column — no
   ResizeObserver/rect math needed like the old fixed hero badge required. */
.idle-panel {
  position: absolute;
  /* cancel .col's own 26px/22px padding so the video fills the column's
     full box, not just its padded content area. */
  inset: -26px -22px;
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
  background: #000;
  opacity: 1;
  transition: opacity 0.35s ease;
}
.idle-panel--hidden { opacity: 0; pointer-events: none; }
.idle-video {
  width: 82.8%; height: 82.8%; /* 50% base, +20%, +20%, +15%, still centered by .idle-panel */
  object-fit: contain;
  display: block;
}

/* error message — red text with a ⚠ warning-triangle icon */
.err {
  color: var(--c-red);
  font-weight: 700;
  font-size: 0.72rem;
  letter-spacing: 0.02em;
  line-height: 1.5;
  display: flex;
  align-items: flex-start;
  gap: 7px;
}
.err::before {
  content: '⚠';
  font-size: 1.05em;
  line-height: 1.3;
  flex-shrink: 0;
}

.muted { color: var(--muted); font-size: 0.56rem; letter-spacing: 0.02em; }
.center { text-align: center; }
.hint { color: var(--muted); font-size: 0.56rem; line-height: 1.6; letter-spacing: 0.02em; margin: 6px 0 0; }
.warn { color: var(--accent); font-size: 0.68rem; line-height: 1.6; }

/* ---- mobile shell (≤860px) ----
   The desktop shell is a fixed-height 2-col grid whose column split is
   written INLINE by columnResize.js (grid-template-columns: 22% 6px 78%),
   which no media-query grid rule can override — on a phone that squeezed
   the nav into a ~22%-wide strip with one Hangul syllable per line. Here
   the shell switches to a plain stacked flex column instead (flex ignores
   the inline grid template entirely) and the page itself scrolls. */
.tool-nav-toggle { display: none; }
@media (max-width: 860px) {
  body.has-chrome .app--shell2,
  .app--shell2 { display: flex; flex-direction: column; height: auto; min-height: calc(100vh - 28px); }
  .col-resizer { display: none; }
  .col { overflow: visible; padding: 20px 16px; }
  .col--nav { border-right: 0; border-bottom: 1px solid var(--border); padding-left: 16px; padding-bottom: 0; }
  .subtitle { margin-bottom: 16px; }
  .tool-nav-item { padding: 12px 16px; margin: 0 -16px; }
  .tool-nav-item:last-child { border-bottom: 0; }
  .tool-nav-desc { white-space: normal; }
  /* hover-fill is sticky on touch (stays lit after a tap) — mute it there */
  @media (hover: none) {
    .tool-nav-item:hover { background-color: transparent; color: var(--text); }
    .tool-nav-item:hover .tool-nav-index,
    .tool-nav-item:hover .tool-nav-desc { color: var(--muted); }
    .tool-nav-item:hover .tool-nav-play::before { opacity: 1; transform: none; }
    .tool-nav-item:hover .tool-nav-play-tri { opacity: 0; }
  }

  /* collapsed nav (a tool is open): only the active row + an expand toggle */
  .tool-nav-toggle {
    display: inline-flex; align-items: center; gap: 4px;
    background: transparent; border: 1px solid rgba(255,255,255,0.22); border-radius: 999px;
    color: var(--text); font: inherit; letter-spacing: 0.06em;
    padding: 3px 10px; cursor: pointer;
  }
  .tool-nav-range { display: none; }
  .col--nav-collapsed .subtitle { display: none; }
  .col--nav-collapsed .tool-nav-item:not(.tool-nav-item--active) { display: none; }

  /* idle coin video: in-flow square below the nav instead of an absolute
     overlay filling a full-height column (there is no full-height column). */
  .idle-panel { position: relative; inset: auto; width: 100%; aspect-ratio: 1; margin: 0; }
  .idle-panel--hidden { display: none; }
  .idle-video { width: 92%; height: 92%; }

  /* corner badge would sit on top of tool controls on a phone */
  .logo-mark { display: none; }
}
/* not enough vertical room under the nav list for a legible badge (short
   desktop window) — see sizeCornerLogo() in app.js */
.logo-mark--shown.logo-mark--cramped { opacity: 0; }
.tool-nav-toggle[hidden] { display: none; }

/* ---- jjaejae.com / Instagram icon links (js/socialLinks.js) ---- */
.social-links { display: flex; align-items: center; gap: 14px; }
.social-links a { display: flex; align-items: center; justify-content: center; width: 18px; height: 18px; opacity: 0.7; transition: opacity 0.15s ease; }
.social-links a:hover { opacity: 1; }
.social-links img { display: block; width: 100%; height: 100%; object-fit: contain; }
/* rounded-square glyph reads larger than the round globe at the same box */
.social-links img.social-ig { width: 88%; height: 88%; }
/* nav: logo left, icons right (right edge = .tool-nav-head's "01 – 10") */
.brand-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 0 0 12px; }
.brand-row .brand { margin: 0; }
/* PIN gate: small, bottom center. In flow as the overlay's last flex child
   (margin-top:auto pins it to the bottom when there's room) rather than
   position:fixed — on a short window the PIN card reaches the bottom edge and
   a fixed row sat on top of it. */
.gate-social { flex-shrink: 0; margin: auto auto 0; padding-top: 22px; gap: 16px; }
.gate-social a { width: 20px; height: 20px; }
