/* ==========================================================================
   Perspicacity — "the graphite studio"
   --------------------------------------------------------------------------
   A dark GRAY room, never pure black. Graphite, concrete, pencil lead, museum
   lighting, soft ambient fog. The interface is the studio the drawing sits in.

   --ink-900 is exactly the WebGL scene's fog colour (#1B1D20 == 0x1b1d20). That
   is load-bearing, not a coincidence: the hero canvas has no border and no
   background of its own, so the room has to continue into the page seamlessly.
   Change one and you must change the other, in studio.js.

   THE ARTWORK IS STILL THE ONLY BRIGHT SURFACE. Everything else is a shade of
   graphite so a drawing on the plate reads as lit paper on a studio wall.

   MEASUREMENT PALETTE. cyan=vertical, yellow=horizontal, orange=depth,
   magenta=diagonal, grey=unjudged, green=horizon, red=vanishing point. Those
   hues are semantic and load-bearing; nothing decorative may use them. On the
   results view only two inks appear: --an-mark, the tutor's red pencil loop, and
   --an-guide, the ruled correction.

   TWO VOICES. Plex Mono carries anything that came out of the geometry; a number
   is never set in the prose face. Newsreader carries anything a person wrote.
   ========================================================================== */

@font-face{font-family:'Plex Mono';src:url('/ui/fonts/plexmono-400.woff2')format('woff2');
  font-weight:400;font-display:swap}
@font-face{font-family:'Plex Mono';src:url('/ui/fonts/plexmono-500.woff2')format('woff2');
  font-weight:500;font-display:swap}
@font-face{font-family:'Newsreader';src:url('/ui/fonts/newsreader-400.woff2')format('woff2');
  font-weight:400;font-display:swap}
@font-face{font-family:'Newsreader';src:url('/ui/fonts/newsreader-500.woff2')format('woff2');
  font-weight:500;font-display:swap}
@font-face{font-family:'Newsreader';src:url('/ui/fonts/newsreader-400i.woff2')format('woff2');
  font-weight:400;font-style:italic;font-display:swap}

:root{
  /* Graphite and concrete. Neutral, faintly warm in the midtones the way real
     concrete is — the old palette was a cool blue-black and read as "tech". */
  --ink-950:#141618;
  --ink-900:#1B1D20;   /* == studio.js C.fog */
  --ink-850:#22252A;
  --ink-800:#2A2E34;
  --ink-700:#343A41;
  --rule:#3B4147;
  --rule-bright:#525A62;

  /* Paper — the artwork. Warm-neutral rag, not the cool grey it was: against
     graphite, a cool paper looked like a screen and a warm one looks like paper. */
  --paper:#EAE8E3;

  --text:#E6E4E0;
  --text-dim:#9DA3A8;
  --text-faint:#6E757C;

  /* Pencil-lead highlight — the colour the sculpture's construction lines glow.
     Used for focus rings and the studio's own UI accents. */
  --lead-light:#C9D6DC;

  /* MEASUREMENT PALETTE — do not reuse decoratively. */
  --vertical:#22E3E3;
  --horizontal:#F5D90A;
  --depth:#FF8A1F;
  --diagonal:#E052E0;
  --unjudged:#809299;
  --horizon:#35D97B;
  --vp:#FF4033;

  --mono:'Plex Mono',ui-monospace,'SF Mono',Menlo,monospace;
  --prose:'Newsreader',Georgia,serif;

  --gut:clamp(20px,4vw,56px);
  --ease:cubic-bezier(.22,.61,.36,1);
  --ease-cine:cubic-bezier(.16,.84,.28,1);
}

*,*::before,*::after{box-sizing:border-box}

/* [hidden] wins, always. Any class that sets `display` outranks the UA
   stylesheet's [hidden]{display:none}, so an element can be "hidden" and still
   laid out. That bit twice in one sitting: the account drawer covered the whole
   page while looking closed, and the no-WebGL fallback's bright paper plate
   collapsed to a 1px line and drew a seam across the hero. One rule, once. */
[hidden]{display:none!important}
html{-webkit-text-size-adjust:100%}
body{
  margin:0;background:var(--ink-900);color:var(--text);
  font-family:var(--prose);font-size:17px;line-height:1.6;
  -webkit-font-smoothing:antialiased;
  /* Soft ambient fog, and a barely-there ruled grain. No hard grid: a graphite
     studio has depth and dust, not graph paper. */
  background-image:
    radial-gradient(120% 80% at 50% -10%,#23262B 0%,transparent 60%),
    radial-gradient(90% 60% at 50% 108%,#101214 0%,transparent 55%),
    repeating-linear-gradient(0deg,rgba(255,255,255,.011) 0 1px,transparent 1px 3px);
  background-attachment:fixed;
}

/* ---- type roles ---------------------------------------------------------
   .m  = machine. Anything measured. Tabular figures so columns of degrees
         line up and a changing number does not jitter.
   .p  = prose. Anything a person wrote.                                    */
.m{font-family:var(--mono);font-variant-numeric:tabular-nums;letter-spacing:-.01em}
.p{font-family:var(--prose)}

.label{
  font-family:var(--mono);font-size:10.5px;font-weight:500;
  text-transform:uppercase;letter-spacing:.16em;color:var(--text-faint);
}

a{color:inherit}
:focus-visible{outline:2px solid var(--lead-light);outline-offset:3px;border-radius:2px}

/* ---- shell -------------------------------------------------------------- */
.shell{max-width:1180px;margin:0 auto;padding:0 var(--gut)}

.topbar{
  display:flex;align-items:baseline;gap:14px;
  padding:22px 0 0;
}
.wordmark{
  font-family:var(--mono);font-weight:500;font-size:13px;
  letter-spacing:.22em;text-transform:uppercase;color:var(--text);
}
.topbar .label{margin-left:auto}

/* ---- the viewfinder: corner brackets + edge rules -----------------------
   The signature. Ticks along the frame are a drafting rule, and the horizon
   readout continues past the artwork into the chrome — the interface is the
   instrument's scale, not a container for a picture. */
.finder{position:relative;margin:26px 0 0;padding:20px}
.finder::before,.finder::after{
  content:'';position:absolute;width:26px;height:26px;pointer-events:none;
  border-color:var(--rule-bright);border-style:solid;
}
.finder::before{top:0;left:0;border-width:1px 0 0 1px}
.finder::after{bottom:0;right:0;border-width:0 1px 1px 0}
.finder-tr,.finder-bl{position:absolute;width:26px;height:26px;pointer-events:none;
  border-color:var(--rule-bright);border-style:solid}
.finder-tr{top:0;right:0;border-width:1px 1px 0 0}
.finder-bl{bottom:0;left:0;border-width:0 0 1px 1px}

/* The plate is capped so the claim below it is on screen at the same time as
   the demonstration. Letterboxing is invisible here: the SVG has no background
   of its own, so any slack simply reads as more paper. */
.plate{
  position:relative;background:var(--paper);border-radius:2px;overflow:hidden;
  box-shadow:0 0 0 1px var(--rule),0 24px 60px -30px #000;
  max-height:min(58vh,520px);
}
.plate svg,.plate img{display:block;width:100%;height:100%;max-height:inherit}

/* ---- hero --------------------------------------------------------------- */
/* Copy and actions sit side by side: the claim is the left column, the thing
   you do about it is the right. On one column the order is claim, then act. */
.hero-copy{
  margin:26px 0 0;display:grid;gap:22px 48px;align-items:end;
  grid-template-columns:minmax(0,1fr) auto;
}
.hero-copy h1{
  font-family:var(--prose);font-weight:400;font-size:clamp(26px,3.2vw,38px);
  line-height:1.14;letter-spacing:-.015em;margin:0 0 10px;text-wrap:balance;
}
/* The measured value is set in the machine face even inside a sentence — the
   two voices meet in the headline, which is the whole argument in one line. */
.hero-copy h1 .m{font-weight:500;font-size:.86em;letter-spacing:-.02em}
.hero-copy p{margin:0;color:var(--text-dim);font-size:16.5px;max-width:52ch}

.cta-row{display:flex;flex-wrap:wrap;gap:12px;align-items:center}
.btn{
  font-family:var(--mono);font-size:13px;font-weight:500;letter-spacing:.06em;
  text-transform:uppercase;border:0;border-radius:2px;padding:14px 22px;
  background:var(--text);color:var(--ink-900);cursor:pointer;
  transition:transform .12s var(--ease),background .12s var(--ease);
}
.btn:hover{transform:translateY(-1px)}
.btn:active{transform:translateY(0)}
.btn-ghost{background:rgba(27,29,32,.5);color:var(--text);
  box-shadow:inset 0 0 0 1px var(--rule-bright);backdrop-filter:blur(2px)}
.btn-ghost:hover{color:var(--text)}
.hint{display:block;margin-top:10px;font-family:var(--mono);font-size:11.5px;
  color:var(--text-faint);letter-spacing:.04em}

/* drag state on the whole plate */
.finder.over .plate{box-shadow:0 0 0 1px var(--vertical),0 24px 60px -30px #000}

/* ---- readout ------------------------------------------------------------ */
.readout{display:grid;gap:1px;background:var(--rule);border:1px solid var(--rule);border-radius:2px}
.readout .cell{background:var(--ink-850);padding:13px 15px}
.readout .cell .label{display:block;margin-bottom:5px}
.readout .cell .v{font-family:var(--mono);font-size:19px;color:var(--text)}
.readout .cell .v small{font-size:11px;color:var(--text-faint);margin-left:4px}

@media(max-width:860px){
  .hero-copy{grid-template-columns:1fr;align-items:start}
  .plate{max-height:none}
  .btn{flex:1 1 auto;text-align:center}
}
@media(prefers-reduced-motion:reduce){
  *{animation-duration:.001ms!important;animation-iteration-count:1!important;
    transition-duration:.001ms!important}
}

/* ---- overlay marks ------------------------------------------------------
   Stroke widths differ per family on purpose: depth lines are the ones being
   judged against the vanishing point, so they read first. */
.ov{fill:none;stroke-linecap:round;stroke-width:2.4;opacity:.95}
.ov.depth{stroke:var(--depth)}
.ov.vert{stroke:var(--vertical);stroke-width:2.1}
.ov.horiz{stroke:var(--horizontal);stroke-width:2.1}
.ov.diag{stroke:var(--diagonal);stroke-width:2.1}
.ov.unjudged{stroke:var(--unjudged);stroke-width:1.6;opacity:.65}

.horizon{stroke:var(--horizon);stroke-width:1.4;stroke-dasharray:1 7;
  stroke-linecap:round;opacity:0;transition:opacity .5s var(--ease) .35s}
.shown .horizon{opacity:.9}

.reticle{opacity:0;transition:opacity .4s var(--ease)}
.shown .reticle{opacity:1}
.ret-ring{fill:none;stroke:var(--vp);stroke-width:1.3;opacity:.9}
.ret-dot{fill:var(--vp)}
.ret-tick{stroke:var(--vp);stroke-width:1.3;stroke-linecap:round}
.shown .ret-ring{animation:pulse 2.6s var(--ease) infinite}
@keyframes pulse{0%,100%{r:13;opacity:.9} 50%{r:17;opacity:.35}}

.hero-stats{display:flex;flex-wrap:wrap;gap:0;margin:18px 0 0;
  border:1px solid var(--rule);border-radius:2px;overflow:hidden}
.hero-stats .s{flex:1 1 auto;min-width:132px;padding:11px 15px;background:var(--ink-850);
  border-right:1px solid var(--rule)}
.hero-stats .s:last-child{border-right:0}
.hero-stats .k{display:block;font-family:var(--mono);font-size:10px;font-weight:500;
  letter-spacing:.16em;text-transform:uppercase;color:var(--text-faint);margin-bottom:4px}
.hero-stats .v{font-family:var(--mono);font-size:17px;font-variant-numeric:tabular-nums}
.hero-stats .v.c-vert{color:var(--vertical)}
.hero-stats .v.c-horiz{color:var(--horizontal)}
.hero-stats .v.c-depth{color:var(--depth)}
.hero-stats .v.c-vp{color:var(--vp)}

/* ==========================================================================
   RESULTS
   The drawing is still the brightest thing on screen and still sits in the
   viewfinder; everything below it is the instrument's readout.
   ========================================================================== */

/* Family swatches are the overlay key, reused verbatim. A reader learns the
   seven hues once, on the drawing, and every later mention matches. */
.fam-vertical{--fam:var(--vertical)}
.fam-horizontal{--fam:var(--horizontal)}
.fam-depth{--fam:var(--depth)}
.fam-diagonal{--fam:var(--diagonal)}
.fam-unjudged{--fam:var(--unjudged)}
.fam-horizon{--fam:var(--horizon)}
.fam-flag{--fam:var(--vp)}

a.wordmark{text-decoration:none}

.verdict{margin:34px 0 0;max-width:62ch}
.verdict h1{
  font-family:var(--prose);font-weight:400;font-size:clamp(27px,3.4vw,40px);
  line-height:1.14;letter-spacing:-.015em;margin:0 0 10px;text-wrap:balance;
}
.verdict p{margin:0;color:var(--text-dim);font-size:16.5px}

/* The results plate hugs its image exactly: the markers are positioned as a
   percentage of it, so any letterboxing would put them in the wrong place. */
.plate-shot{max-height:none;overflow:visible}
.plate-shot img{height:auto;max-height:none;border-radius:2px}

/* A marker is a number on the drawing and the same number in the list. It is
   the only handle the reader gets — coordinates are the engine's language,
   not an artist's. */
.mk{
  position:absolute;transform:translate(-50%,-50%);
  min-width:22px;height:22px;padding:0 5px;border-radius:11px;
  display:grid;place-items:center;
  font-family:var(--mono);font-size:12px;font-weight:500;line-height:1;
  background:var(--ink-900);color:var(--fam,var(--text));
  box-shadow:0 0 0 1.5px var(--fam,var(--text)),0 2px 8px #0009;
  cursor:pointer;transition:transform .14s var(--ease);
}
.mk.hot{transform:translate(-50%,-50%) scale(1.32)}

h2{
  font-family:var(--mono);font-size:10.5px;font-weight:500;text-transform:uppercase;
  letter-spacing:.16em;color:var(--text-faint);margin:44px 0 14px;
}
section{margin:0}

.key{display:flex;flex-wrap:wrap;gap:8px 20px;margin:14px 0 0;
  font-family:var(--mono);font-size:11px;letter-spacing:.06em;color:var(--text-faint)}
.key i{display:inline-block;width:16px;height:2px;border-radius:1px;
  vertical-align:.28em;margin-right:7px;background:var(--fam)}

.offlist{list-style:none;margin:0;padding:0;
  border-top:1px solid var(--rule)}
.off{display:grid;grid-template-columns:auto 1fr auto;gap:0 14px;align-items:baseline;
  padding:14px 8px;border-bottom:1px solid var(--rule);cursor:default;
  transition:background .14s var(--ease)}
.off.hot{background:var(--ink-850)}
.mknum{
  font-family:var(--mono);font-size:12px;font-weight:500;line-height:1;
  min-width:22px;height:22px;padding:0 5px;border-radius:11px;
  display:grid;place-items:center;align-self:center;
  color:var(--fam);box-shadow:inset 0 0 0 1.5px var(--fam);
}
.off-what{font-family:var(--prose);font-size:16.5px}
.off-what b{font-weight:500}
.off-rule{display:block;font-family:var(--mono);font-size:11px;letter-spacing:.04em;
  color:var(--text-faint);margin-top:3px}
.off-deg{font-size:17px;color:var(--text);white-space:nowrap}

table{width:100%;border-collapse:collapse;font-size:15px}
th{text-align:left;font-family:var(--mono);font-size:10px;font-weight:500;
  text-transform:uppercase;letter-spacing:.14em;color:var(--text-faint);
  padding:0 12px 9px 0;border-bottom:1px solid var(--rule)}
td{padding:14px 12px 14px 0;border-bottom:1px solid var(--rule);vertical-align:top}
td .swatch{display:inline-block;width:16px;height:2px;border-radius:1px;
  background:var(--fam);vertical-align:.3em;margin-right:9px}
td b{font-weight:500}
td .off-rule{margin-left:25px}

.pill{font-family:var(--mono);font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;
  padding:4px 10px;border-radius:2px;white-space:nowrap;
  color:var(--text-faint);box-shadow:inset 0 0 0 1px var(--rule-bright)}
.pill.ok{color:var(--horizon);box-shadow:inset 0 0 0 1px #1E5C39}
.pill.bad{color:var(--depth);box-shadow:inset 0 0 0 1px #6B3A10}

.plainlist{margin:0;padding-left:20px;color:var(--text-dim)}
.plainlist li{margin:10px 0}
.foot-note{font-family:var(--mono);font-size:11.5px;color:var(--text-faint);
  letter-spacing:.04em;margin:14px 0 0}

/* Silence is the product's most distinctive behaviour, but it is not the
   headline — it belongs one click away, present and not insisted on. */
.quiet{margin:44px 0 0;border-top:1px solid var(--rule);padding-top:16px}
.quiet summary{font-family:var(--mono);font-size:10.5px;font-weight:500;
  text-transform:uppercase;letter-spacing:.16em;color:var(--text-faint);
  cursor:pointer;list-style:none}
.quiet summary::-webkit-details-marker{display:none}
.quiet summary::before{content:'+ ';color:var(--text-faint)}
.quiet[open] summary::before{content:'– '}
.quiet ul{margin:16px 0 0;padding-left:20px;color:var(--text-dim);max-width:70ch}
.quiet li{margin:12px 0;font-size:15.5px}
.quiet b{color:var(--text);font-weight:500}

.again{margin:48px 0 56px;border-top:1px solid var(--rule);padding-top:18px}
.again a{font-family:var(--mono);font-size:12px;font-weight:500;letter-spacing:.12em;
  text-transform:uppercase;color:var(--text-dim);text-decoration:none}
.again a:hover{color:var(--text)}

@media(max-width:640px){
  .off{grid-template-columns:auto 1fr;row-gap:6px}
  .off-deg{grid-column:2;justify-self:start}
}
/* The tagline is the first thing to go: it restates the wordmark, and at this
   width it pushes past the edge rather than wrapping. */
@media(max-width:560px){
  .topbar .label{display:none}
}

/* ==========================================================================
   SCANNING
   The wait is not dead time: it is the first moment the user sees their own
   drawing on the lit plate, which is the whole promise of the product. The
   sweep is an indeterminate indicator on purpose -- the engine reports no
   progress, so a percentage or a ticking stage list would be invented.
   ========================================================================== */
.scan-only{display:none}
.scanning .hero-copy,.scanning .hero-stats{display:none}
.scanning .scan-only{display:block}
/* A fixed box during the scan: an uploaded portrait sketch is otherwise taller
   than the screen, and the plate's max-height would crop it rather than fit it. */
.scanning .plate{height:min(58vh,520px);max-height:none}
.scanning .plate img{width:100%;height:100%;max-height:none;object-fit:contain}

.plate .sweep{
  position:absolute;left:0;right:0;height:18%;pointer-events:none;
  /* A trailing wash under one bright rule, not a broad tint: the line is the
     instrument passing over the paper, and it must not recolour the drawing. */
  background:linear-gradient(to bottom,
    transparent 0%, #22E3E30F 62%, #22E3E333 92%, var(--vertical) 100%);
  mix-blend-mode:multiply;
  animation:sweep 1.9s linear infinite;
}
@keyframes sweep{from{top:-18%}to{top:100%}}

.scan-head{margin:26px 0 0}
.scan-head h2{margin:0 0 8px}
.scan-file{font-family:var(--mono);font-size:12px;color:var(--text-dim);
  letter-spacing:.04em;word-break:break-all}
.stages{list-style:none;margin:16px 0 0;padding:0;display:flex;flex-wrap:wrap;gap:8px}
.stages li{font-family:var(--mono);font-size:11px;letter-spacing:.08em;
  text-transform:uppercase;color:var(--text-faint);
  padding:6px 11px;box-shadow:inset 0 0 0 1px var(--rule)}

@media(prefers-reduced-motion:reduce){
  .plate .sweep{animation:none;top:41%;opacity:.5}
}

/* ==========================================================================
   PHASE 6 — THE RESULTS STAGE
   --------------------------------------------------------------------------
   The artwork moves left, the panel arrives on the right, and the drawing never
   leaves the screen. The panel is sticky so a tall drawing can scroll past it
   without the feedback going with it.

   A NOTE ON THE PALETTE. The family key above still governs the older
   server-rendered page, which draws every graded line. This view does not: it
   shows a handful of findings, so two inks carry the whole language.

     --an-mark   the OBSERVATION. A tutor's red pencil loop. Hand-drawn.
     --an-guide  the CORRECTION. Ruled, cool, obviously not pencil, because it
                 is the geometry rather than an opinion about it.

   Eye level stays green and the vanishing point stays red, unchanged in meaning
   from the key above.
   ========================================================================== */
:root{
  --an-mark:var(--vp);
  --an-guide:var(--vertical);
}

/* ---- stage layout ------------------------------------------------------- */
.stage{display:flex;gap:0;align-items:flex-start}
.art-col{flex:1 1 auto;min-width:0}
.panel{
  flex:0 0 0;width:0;opacity:0;overflow:hidden;
  transition:width .62s var(--ease),opacity .42s var(--ease) .16s,
             padding .62s var(--ease);
}
body.results .stage{gap:0}
body.results .panel{
  flex:0 0 min(430px,40vw);width:min(430px,40vw);opacity:1;
  padding-left:clamp(20px,2.6vw,40px);
  position:sticky;top:20px;max-height:calc(100vh - 40px);overflow-y:auto;
  /* The scrollbar is the panel's own, so the artwork stays put. */
  scrollbar-width:thin;
}

/* "Measure another drawing" sits above the artwork, not below the analysis. */
.stage-bar{display:none;justify-content:flex-end;margin:20px 0 0}
body.results .stage-bar{display:flex}
.btn-again{background:var(--lead-light);color:var(--ink-900)}
.btn-again:hover{background:#E2ECF0}

/* The pitch and the essay are for people who have not uploaded anything yet. */
body.scanning #hero-copy,body.results #hero-copy,
body.scanning #prose,body.results #prose{display:none}

/* ---- the annotated plate ------------------------------------------------ */
body.results .plate{max-height:none;overflow:visible}
/* height:auto is load-bearing: the overlay maps its viewBox 1:1 onto this box,
   so any letterboxing would put every mark in the wrong place. */
body.results .plate img{width:100%;height:auto;max-height:none;display:block}
.plate img[hidden]{display:none}

.marks{
  position:absolute;inset:0;width:100%;height:100%;
  pointer-events:none;overflow:visible;
}
.marks *{pointer-events:none}
.an-loop{pointer-events:stroke}

/* the observation — hand-drawn */
.an-loop{
  fill:none;stroke:var(--an-mark);stroke-width:2.6;
  stroke-linecap:round;stroke-linejoin:round;opacity:.92;
}
.an.hot .an-loop{stroke-width:3.6;opacity:1}

/* the correction — ruled */
.an-guide{
  fill:none;stroke:var(--an-guide);stroke-width:1.4;
  stroke-dasharray:7 6;opacity:0;transition:opacity .4s var(--ease);
}
.an.hot .an-guide{opacity:.95}

/* instrument marks */
.an-horizon{stroke:var(--horizon);stroke-width:1.3;stroke-dasharray:2 8;
  stroke-linecap:round;opacity:.75;vector-effect:non-scaling-stroke}
.an-vp-ring{fill:none;stroke:var(--vp);stroke-width:1.3;opacity:.85;
  vector-effect:non-scaling-stroke}
.an-vp-dot{fill:var(--vp)}
.an-vp-tick{stroke:var(--vp);stroke-width:1.3;stroke-linecap:round;
  vector-effect:non-scaling-stroke}

/* ---- the panel ---------------------------------------------------------- */
.v-head{margin:0 0 26px}
.v-eyebrow{
  display:block;font-family:var(--mono);font-size:10.5px;font-weight:500;
  text-transform:uppercase;letter-spacing:.16em;color:var(--text-faint);
  margin-bottom:12px;
}
.panel[data-state="polished"] .v-eyebrow{color:var(--horizon)}
.panel[data-state="no-reference"] .v-eyebrow{color:var(--text-dim)}
/* text-transform and letter-spacing are reset explicitly: the older
   server-rendered page styles bare h2 as a mono uppercase eyebrow, and this is
   an h2 too. Relying on source order to win that would be a trap for the next
   person who moves a rule. */
.v-title{
  font-family:var(--prose);font-weight:400;font-size:clamp(23px,2.1vw,29px);
  line-height:1.18;letter-spacing:-.015em;margin:0 0 12px;text-wrap:balance;
  text-transform:none;color:var(--text);
}
.v-body{margin:0;color:var(--text-dim);font-size:16px;line-height:1.62}

.cards{display:grid;gap:1px;background:var(--rule);
  border-top:1px solid var(--rule);border-bottom:1px solid var(--rule)}
.card{
  background:var(--ink-850);padding:17px 18px;cursor:default;
  transition:background .16s var(--ease);
}
.card.hot{background:var(--ink-800)}
/* Location first and in the mark's own ink, so the card and the loop on the
   drawing are obviously the same thing; the kind follows as a quiet tag. */
.card-head{display:flex;align-items:baseline;gap:9px;margin-bottom:9px;
  flex-wrap:wrap}
.card-where{
  font-family:var(--mono);font-size:11px;font-weight:500;letter-spacing:.14em;
  text-transform:uppercase;color:var(--an-mark);margin:0;
}
.card-kind{
  font-family:var(--mono);font-size:9.5px;font-weight:500;
  text-transform:uppercase;letter-spacing:.16em;color:var(--text-faint);
}
.card-kind::before{content:'· '}
.card-title{
  font-family:var(--prose);font-weight:400;font-size:18px;line-height:1.28;
  margin:0 0 5px;letter-spacing:-.01em;
}
.card-detail{margin:0;color:var(--text-dim);font-size:14.5px;line-height:1.6}

.panel-foot{
  font-family:var(--mono);font-size:10.5px;letter-spacing:.08em;
  color:var(--text-faint);margin:26px 0 40px;word-break:break-all;
}

/* ---- advanced analytics ------------------------------------------------- */
/* The precision, filed rather than softened. Everything stages 1-5 measured is
   in here verbatim, degrees and coordinates included. */
.adv{margin:26px 0 0;border-top:1px solid var(--rule);padding-top:16px}
.adv summary{
  font-family:var(--mono);font-size:10.5px;font-weight:500;text-transform:uppercase;
  letter-spacing:.16em;color:var(--text-dim);cursor:pointer;list-style:none;
}
.adv summary::-webkit-details-marker{display:none}
.adv summary::before{content:'+ ';color:var(--text-faint)}
.adv[open] summary::before{content:'– '}
.adv summary:hover{color:var(--text)}
.adv-body{margin-top:18px}
.adv-group{margin:0 0 24px}
.adv-group h4{
  font-family:var(--mono);font-size:9.5px;font-weight:500;text-transform:uppercase;
  letter-spacing:.16em;color:var(--text-faint);margin:0 0 9px;
}
.adv-row{display:flex;justify-content:space-between;gap:14px;
  padding:5px 0;border-bottom:1px solid var(--ink-800);font-size:13px}
.adv-k{color:var(--text-faint);font-family:var(--mono);font-size:11px;
  letter-spacing:.05em;white-space:pre}
.adv-v{color:var(--text-dim);font-size:12.5px}
.adv-table{width:100%;border-collapse:collapse;font-size:12.5px}
.adv-table th{font-family:var(--mono);font-size:9px;letter-spacing:.14em;
  text-transform:uppercase;color:var(--text-faint);text-align:left;
  padding:0 8px 6px 0;border-bottom:1px solid var(--rule);font-weight:500}
.adv-table td{padding:6px 8px 6px 0;border-bottom:1px solid var(--ink-800);
  color:var(--text-dim)}
.adv-list{margin:0;padding-left:18px;color:var(--text-dim);font-size:13px}
.adv-list li{margin:8px 0;line-height:1.55}

/* ---- homepage prose ----------------------------------------------------- */
/* And a matching soft entry on the copy below, so the eye never meets an edge
   between the room and the page. */
.prose{max-width:60ch;margin:76px 0 0;position:relative}
.prose::before{
  content:'';position:absolute;left:50%;top:-96px;transform:translateX(-50%);
  width:100vw;height:140px;pointer-events:none;
  background:linear-gradient(to bottom,var(--ink-900),rgba(27,29,32,0));
}
.prose h2{
  font-family:var(--mono);font-size:10.5px;font-weight:500;text-transform:uppercase;
  letter-spacing:.16em;color:var(--text-faint);margin:0 0 20px;
}
.prose p{margin:0 0 18px;font-size:17px;line-height:1.68;color:var(--text-dim)}
.prose p:first-of-type{color:var(--text);font-size:19px;line-height:1.6}

footer{margin:76px 0 44px;border-top:1px solid var(--rule);padding-top:18px}

/* ---- narrow ------------------------------------------------------------- */
@media(max-width:900px){
  .stage{flex-direction:column}
  body.results .panel{
    flex:1 1 auto;width:100%;padding-left:0;position:static;
    max-height:none;overflow:visible;margin-top:26px;
  }
  .stage-bar{justify-content:stretch}
  .btn-again{width:100%}
}

/* ==========================================================================
   THE STUDIO — nav, the sculpture's room, and the device-local drawer
   ========================================================================== */
.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;border:0}

/* ---- navigation, on every page ----------------------------------------- */
.nav{
  position:fixed;top:0;left:0;right:0;z-index:40;
  display:flex;align-items:center;gap:clamp(16px,3vw,42px);
  padding:18px clamp(20px,4vw,56px);
  /* The room shows through: the nav is a plate of glass in front of the studio,
     not a bar sitting on top of it. */
  background:linear-gradient(to bottom,rgba(27,29,32,.92),rgba(27,29,32,0));
  backdrop-filter:blur(6px);
  -webkit-backdrop-filter:blur(6px);
}
.nav .wordmark{text-decoration:none;font-size:13px}
/* The wordmark and its tagline read as one unit, tighter than the nav's own gap.
   Baseline-aligned so the small tag sits on the wordmark's baseline. */
.brand{display:flex;align-items:baseline}
.wordmark-tag{
  font-family:var(--mono);font-size:10px;font-weight:500;letter-spacing:.16em;
  text-transform:uppercase;color:var(--text-faint);white-space:nowrap;
}
.wordmark-tag::before{content:'·';margin:0 9px;opacity:.6}
.nav-mid{display:flex;gap:clamp(14px,2.4vw,30px);margin-left:auto}
.nav-end{display:flex;gap:18px}
.nav a[data-view]{
  font-family:var(--mono);font-size:11px;font-weight:500;letter-spacing:.15em;
  text-transform:uppercase;color:var(--text-faint);text-decoration:none;
  transition:color .18s var(--ease);
}
.nav a[data-view]:hover{color:var(--text)}

/* ---- the sculpture's room ---------------------------------------------- */
/* Full viewport, no frame, no border. The canvas background is exactly
   --ink-900, so the room simply continues into the page. */
.studio{
  position:relative;height:100svh;min-height:560px;
  display:flex;align-items:flex-end;justify-content:center;
  overflow:hidden;
}
.studio canvas{
  position:absolute;inset:0;width:100%;height:100%;display:block;
}
.studio-fallback{position:absolute;inset:0;display:grid;place-items:center;
  padding:96px clamp(20px,4vw,56px) 180px}
.plate-demo{width:min(1000px,92vw);max-height:60vh}

/* A scrim under the controls. The near concrete is lit, and uppercase mono at
   11px over it was not holding contrast — this is legibility, not decoration. */
/* The scrim lands EXACTLY on the page colour, and travels a long way to get
   there. It used to bottom out at rgba(20,22,24,.88) — darker than --ink-900 —
   so the studio ended darker than the page began and you got a visible step from
   dark grey to mid grey right under the buttons. Now the last stop IS --ink-900,
   over 52% of the section, so the room dissolves into the page instead of
   stopping at it. It still darkens behind the controls, which is its other job. */
.studio::before{
  content:'';position:absolute;left:0;right:0;bottom:0;height:52%;
  background:linear-gradient(to top,
    var(--ink-900) 0%,
    rgba(27,29,32,.94) 14%,
    rgba(27,29,32,.72) 34%,
    rgba(27,29,32,.34) 62%,
    rgba(27,29,32,0) 100%);
  pointer-events:none;z-index:1;
}
.studio-cta{
  position:relative;z-index:2;text-align:center;
  padding:0 20px clamp(46px,7vh,86px);
  display:flex;flex-direction:column;align-items:center;gap:14px;
}
.studio-cta .btn{min-width:200px}
.btn-lead{background:var(--lead-light);color:var(--ink-950)}
.btn-lead:hover{background:#E4EDF1}
.studio-hint{
  margin:2px 0 0;font-family:var(--mono);font-size:11px;letter-spacing:.1em;
  text-transform:uppercase;color:var(--text-faint);
}
.studio-cta{flex-direction:row;flex-wrap:wrap;justify-content:center}
.studio-cta .studio-hint{flex-basis:100%}

/* Cinematic, not bouncy: the buttons settle rather than pop. */
.studio-cta .btn{transition:transform .5s var(--ease-cine),background .3s var(--ease)}
.studio-cta .btn:hover{transform:translateY(-2px)}

/* Once a drawing is in play the room steps aside entirely. */
body.scanning .studio,body.results .studio{display:none}
body.scanning .prose,body.results .prose{display:none}

/* The results stage only exists once there is something in it. */
.shell{padding-top:4px}
body:not(.scanning):not(.results) .stage,
body:not(.scanning):not(.results) .stage-bar{display:none}
body.scanning .shell,body.results .shell{padding-top:86px}

/* drag anywhere */
body.dropping .studio::after{
  content:'';position:absolute;inset:18px;pointer-events:none;
  border:1px dashed var(--lead-light);opacity:.5;border-radius:3px;
}

/* ---- the device-local drawer ------------------------------------------- */
.drawer{
  position:fixed;inset:0;z-index:60;background:rgba(12,13,15,.72);
  backdrop-filter:blur(3px);display:flex;justify-content:flex-end;
}
.drawer-in{
  width:min(460px,92vw);height:100%;overflow-y:auto;
  background:var(--ink-900);border-left:1px solid var(--rule);
  padding:26px clamp(20px,3vw,32px) 60px;
  box-shadow:-30px 0 80px -40px #000;
}
.drawer-x{
  float:right;background:none;border:0;color:var(--text-faint);
  font-size:26px;line-height:1;cursor:pointer;padding:0 0 10px 10px;
}
.drawer-x:hover{color:var(--text)}

@media(max-width:700px){
  .nav{gap:12px;padding:14px 20px}
  .nav-mid{gap:14px}
  .nav a[data-view]{font-size:10px;letter-spacing:.1em}
  .wordmark-tag{display:none}
  .studio{height:92svh}
  .studio-cta .btn{min-width:0;flex:1 1 46%}
}
@media(prefers-reduced-motion:reduce){
  .studio-cta .btn{transition:none}
}

/* Mobile nav and controls. At 375px the wordmark crowded the links, both link
   labels wrapped onto two lines each, and "Upload your art" broke mid-phrase. */
@media(max-width:560px){
  .nav{gap:10px;padding:12px 16px}
  .nav .wordmark{font-size:10.5px;letter-spacing:.14em}
  .nav-mid{gap:12px}
  .nav-end{gap:10px}
  .nav a[data-view]{font-size:9.5px;letter-spacing:.08em;white-space:nowrap}
  .studio-cta{padding-bottom:clamp(34px,5vh,54px);gap:10px}
  .studio-cta .btn{font-size:11.5px;padding:13px 12px;white-space:nowrap}
  .studio-hint{font-size:9.5px;letter-spacing:.07em}
}

/* ---- 1. arrival, and 7. the controls rising -----------------------------
   The room fades up out of the dark instead of snapping on. The veil is a plain
   opaque plate over the canvas — cheaper and more reliable than animating
   exposure or fog inside the scene, and it covers the first frame while the
   font and the shadow map are still being built. */
.studio-veil{
  position:absolute;inset:0;z-index:3;pointer-events:none;
  background:#101214;opacity:1;
  transition:opacity 1.6s var(--ease-cine);
}
.studio-veil.gone{opacity:0}

/* The end-of-sequence prompt. Centred on the studio, which is where the one-point
   vanishing point projects, so it lands exactly where the letters vanished. It is
   driven entirely by GSAP (opacity + a horizontal squish as it is crushed), so the
   wrap owns the centring and the text owns nothing but its own transform. */
.studio-endwrap{
  position:absolute;inset:0;z-index:3;
  display:grid;place-items:center;pointer-events:none;
}
.studio-endline{
  margin:0;transform-origin:center center;opacity:0;
  font-family:var(--mono);font-size:clamp(11px,1.6vw,15px);
  letter-spacing:.3em;text-transform:uppercase;color:var(--lead-light);
  white-space:nowrap;text-align:center;
  text-shadow:0 2px 24px rgba(10,12,14,.9);
}
@media(max-width:560px){
  .studio-endline{font-size:10px;letter-spacing:.16em;white-space:normal;
    max-width:78vw;line-height:1.7}
}
@media(prefers-reduced-motion:reduce){
  .studio-endline{display:none}
}

/* Controls rise into place, and step aside while the sequence plays. */
.studio-cta{
  opacity:0;transform:translateY(18px);
  transition:opacity .9s var(--ease-cine),transform 1.1s var(--ease-cine);
}
.studio-cta.up{opacity:1;transform:none}

@media(prefers-reduced-motion:reduce){
  .studio-veil{transition:none;opacity:0}
  .studio-cta{transition:none;opacity:1;transform:none}
}

/* ---- film grade over the studio ----------------------------------------
   A vignette and a fine grain, in CSS rather than a WebGL post pass: it needs no
   extra vendored files, costs one composited layer, and does the same job. Real
   footage of a dark room is never perfectly clean or evenly lit, and a pristine
   render is most of what makes 3D look like 3D. */
.studio-grade{
  position:absolute;inset:0;z-index:2;pointer-events:none;
  background:
    radial-gradient(125% 95% at 50% 42%,transparent 40%,rgba(5,6,7,.6) 100%),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='.16'/%3E%3C/svg%3E");
  background-blend-mode:normal,overlay;
  /* At 0.5 grain over soft-light the whole frame went milky and the stone read
     pale — grain has to sit under the image, not on top of it. */
  opacity:.62;
}
/* the controls sit above the grade */
.studio-cta{z-index:3}
