/* ============================================================
   CONTACT SHEET — cluster theme for pixelbench.
   Loaded AFTER atlas.css. Keeps the structural system (plates,
   key/plate layout, ruled rows, clip-path bars, ghost trace,
   pinned mobile bar) and replaces the material world.

   The world: a photographic contact sheet and the film rebate —
   the edge strip carrying frame numbers and stock markings. It
   earns its place rather than decorating: the rebate's tracked
   uppercase micro-lettering IS the legend lettering the system
   already defines, and film frame numbers replace plate numbers
   natively. Where the atlas is engraved paper, this is a working
   darkroom bench.

   Deliberately NOT dark-mode-with-a-neon-accent. A darkroom is
   dark; a contact sheet is not. The sheet is the page, the film
   is the ink.

   Contrast measured against both grounds before shipping —
   figures in the table below, and --chinagraph-ink was darkened
   from #b03f12 (4.41 on --paper-2, under AA) to #a53a10 (4.90).
   ============================================================ */

:root{
  /* ground — photographic paper, cool and clean. A step wedge, not a
     cream editorial ground: the neutrals are genuinely neutral so the
     one signal colour has all the room. */
  --paper:#f1f2f3;
  --paper-2:#e2e4e6;
  /* ink — cool near-black, a true D-max */
  --ink:#15171a;
  --ink-2:#4b5054;
  --ink-3:#575c61;
  --rule:#9ba1a6;
  --rule-2:#c6cacd;

  /* plate inks — remapped, so every atlas rule keeps working unchanged.

     The pencil is BLUE. Chinagraph came in red, blue, yellow and white,
     and blue is as authentic on a contact sheet as red — but red-orange
     on warm neutrals is the single most over-used accent on the web
     right now, and it made this read as generic rather than as an
     instrument. One saturated signal against true greys is what a
     densitometer, a loupe or a scanner UI actually looks like.

     Ratios below are measured against --paper / --paper-2 and are
     re-checked in the browser, not asserted here. */
  --prussian:#111417;       /* film rebate black — the plate field */
  --vermillion:#1668b8;     /* chinagraph fill — the grease pencil */
  --vermillion-ink:#12558f; /* 6.88 / 6.05 — the text tone */
  --ochre:#c8830f;          /* safelight amber — tight, working */
  --ochre-ink:#79500a;      /* 5.72 / 5.03 */
  --olive:#2f4858;          /* instrument slate — the budget bar */
  --olive-ink:#2b4351;      /* 9.13 / 8.02 */

  /* semantic aliases — the names the spec and the markup use */
  --field:var(--prussian);
  --chinagraph:var(--vermillion);
  --chinagraph-ink:var(--vermillion-ink);
  --safelight:var(--ochre);
  --tray:var(--olive);
  --on-field:#eef0f1;       /* 16.4:1 on --field */
  --on-field-signal:#6bb2ee;/* 7.8:1 on --field */
}

/* ── the sprocket rule ───────────────────────────────────────
   Perforations along a section break, replacing a plain hairline.
   Pure CSS, no image. Used twice per page at most — a signature,
   not a texture. */
.sprocket{
  height:9px;
  border:0;
  margin:2.5rem 0;
  background:
    repeating-linear-gradient(to right,
      var(--rule) 0 7px, transparent 7px 15px) center/100% 1px no-repeat,
    repeating-linear-gradient(to right,
      var(--rule-2) 0 6px, transparent 6px 14px) center/100% 9px no-repeat;
}

/* ── masthead as film rebate ─────────────────────────────────
   The strip along the edge of a roll: stock name, frame ticks.

   It was padding-block:.55rem, which rendered a 43px sliver on desktop —
   too thin to read as a band at all, so it looked like a stray rule
   above the page rather than the edge of the film. The rebate is the
   widest part of the stock; it needs to hold its own height. */
.masthead{
  background:var(--field);
  border-bottom:2px solid var(--ink);
  color:var(--on-field);
  padding:1rem 1.5rem 1.15rem;
  position:relative;
}
/* real perforations, not a faint dotted hairline: a sprocket hole is a
   punched rectangle with visible width, and at .35 opacity the old strip
   was barely present on the darkest ground on the site. */
.masthead::after{
  content:"";
  position:absolute; inset-inline:0; bottom:0; height:7px;
  background:repeating-linear-gradient(to right,
    var(--on-field) 0 9px, transparent 9px 22px);
  opacity:.5;
}
.masthead a,.masthead .legend{ color:var(--on-field); }
.masthead .legend-ink{ color:var(--on-field-signal); }

/* Phone: brand and nav share one line, with the nav as a strip that
   scrolls sideways. Stacking the brand above a 44px touch-target nav
   costs a guaranteed ~128px — 16% of the screen spent before the tool
   — and the nav row alone already sets the floor, so the brand may as
   well sit beside it for free.

   The links overflow deliberately. A strip that runs under the right
   edge reads as scrollable; a wrapped block reads as a menu and takes
   three lines to say so. */
@media (max-width:40rem){
  .masthead{
    display:flex; align-items:center; gap:.85rem;
    /* the base rule sets flex-wrap:wrap; without undoing it the nav is
       wider than the brand leaves room for and drops to its own line,
       which is the whole problem being fixed */
    flex-wrap:nowrap;
    padding:.3rem 1.25rem .45rem;
  }
  .masthead .brand{ flex:0 0 auto; margin-bottom:0; }
  .masthead nav{
    flex:1 1 auto; min-width:0;
    display:flex; flex-wrap:nowrap; gap:1.1rem;
    overflow-x:auto; scrollbar-width:none;
    /* bleed to the edge so a half-cut link is the affordance */
    margin-inline-end:-1.25rem; padding-inline-end:1.25rem;
  }
  .masthead nav::-webkit-scrollbar{ display:none }
  .masthead nav a{ white-space:nowrap; flex:0 0 auto }
}

/* ── getting to the tool ─────────────────────────────────────
   The masthead, heading and standfirst were spending 304px before
   the first control on a 720px laptop, which pushed the drop zone —
   the one thing a visitor arrives here to use — past the fold. The
   type scale is not the problem and is left alone; the air around it
   is. Widening the standfirst measure drops it from three lines to
   two, which costs nothing and reads better anyway.

   .key keeps its padding-top: that is what aligns the top of the form
   with the top of the plate beside it, and removing it to save 20px
   would break the one alignment holding the two columns together. */
.title{ padding-block-start:1.25rem; }
.standfirst{ max-width:78ch; margin-bottom:.75rem; }

/* ── the drop zone ───────────────────────────────────────────
   A mounting area, ruled not boxed. Dashed because it is where
   something is about to be laid down, which is the one place a
   dashed rule carries meaning rather than decorating.

   The border was --rule (#a8abac on #eceded = 1.97:1) — the lowest-
   contrast element on the page, on the control the whole task starts
   from, failing WCAG 1.4.11's 3:1 for non-text UI. It is --ink-2 now
   (6.62:1) and the lead line carries real weight, so the invitation
   is the most visible thing in the key. */
.drop{
  display:block;
  border:2px dashed var(--ink-2);
  background:var(--paper-2);
  padding:1.75rem 1.25rem;
  text-align:center;
  cursor:pointer;
  transition:border-color 140ms var(--ease), background 140ms var(--ease);
}
.drop-lead{
  display:block; font-size:1.0625rem; font-weight:640; color:var(--ink);
  letter-spacing:-.01em;
}
.drop-sub{ display:block; font-size:.875rem; color:var(--ink-2); margin-top:.25rem; }
.drop-formats{
  display:block; margin-top:.7rem;
  font-size:.6875rem; letter-spacing:.1em; text-transform:uppercase;
  font-weight:600; color:var(--ink-3);
}
.drop:hover{ border-color:var(--chinagraph); background:var(--paper); }
.drop.is-over{ border-color:var(--chinagraph); border-style:solid; background:var(--paper); }
/* the zone is a <label> for a visually-hidden file input, so the ring has to
   come from the input's focus — a label never receives :focus-visible */
input[type="file"]:focus-visible + .drop{
  outline:2px solid var(--chinagraph); outline-offset:1px;
  border-color:var(--ink-2); background:var(--paper);
}
.drop[aria-disabled="true"]{ opacity:.55; cursor:not-allowed; }

/* ── size presets ────────────────────────────────────────────
   The guide names the limits people actually meet; typing "2 MB"
   on a phone keyboard is friction with no upside. The free field
   stays the source of truth — these just fill it. */
.presets{ display:flex; flex-wrap:wrap; gap:.4rem; margin:.6rem 0 .2rem; }
.presets button{
  font:inherit; font-size:.8125rem; font-weight:600;
  padding:.45rem .7rem; min-height:44px;
  background:var(--paper); color:var(--ink-2);
  border:1px solid var(--rule); cursor:pointer;
  font-variant-numeric:tabular-nums lining-nums;
  transition:border-color 140ms var(--ease), color 140ms var(--ease);
}
.presets button:hover{ border-color:var(--ink-2); color:var(--ink); }
.presets button:focus-visible{ outline:2px solid var(--chinagraph); outline-offset:1px; }
/* selection carries weight and a rule, never colour alone */
.presets button[aria-pressed="true"]{
  border:2px solid var(--ink); color:var(--ink); background:var(--paper-2);
}

/* the zip action sits in the plate head, so the head has to hold it */
.plate-head:has(#zipall:not([hidden])){ display:flex; align-items:center; gap:.75rem; flex-wrap:wrap; }
#zipall{ margin-left:auto; }

/* ── contact sheet frames ────────────────────────────────────
   Before and after, mounted as two frames on the sheet with the
   figures set in rebate lettering beneath — which is exactly how
   a contact sheet is annotated. */
.frames{
  display:grid;
  grid-template-columns:repeat(auto-fit,minmax(11rem,1fr));
  gap:1.25rem;
  margin-block:1.25rem;
}
.frame{ position:relative; margin:0; }
.frame-img{
  display:block; width:100%; height:auto;
  background:var(--field);
  border:1px solid var(--ink);
  /* the image sits on the rebate, as a frame does on film */
  padding:5px;
}
.frame-cap{
  display:flex; justify-content:space-between; gap:.75rem;
  margin-top:.4rem;
  font-size:.6875rem; letter-spacing:.14em; text-transform:uppercase;
  font-weight:600; color:var(--ink-2);
}
.frame-cap b{ color:var(--ink); font-weight:700; font-variant-numeric:tabular-nums lining-nums; }

/* the grease-pencil ring — SVG stroke, never a border-radius, so
   the no-radius rule holds and the mark reads as hand-made */
.frame-ring{
  position:absolute; inset:-6px; width:calc(100% + 12px); height:calc(100% + 12px);
  pointer-events:none; overflow:visible; z-index:1;
}
.frame-ring path{
  fill:none; stroke:var(--chinagraph); stroke-width:3.5;
  stroke-linecap:round; opacity:.85;
}
.frame[data-chosen="true"] .frame-cap b{ color:var(--chinagraph-ink); }

/* ── budget bar ──────────────────────────────────────────────
   Reuses the atlas bar mechanics (clip-path reveal) entirely —
   only the inks change.

   The track has to be DRAWN here, which it is not in atlas.css.
   There, a track holds waterfall segments measured against a
   scale printed elsewhere, so an unpainted track is correct.
   Here the track has one fixed meaning — it IS the budget, and
   its right edge is the limit. Leaving it invisible left the
   fill floating with nothing marking where the target ended,
   which is the one thing this tool exists to show. */
.bar-budget-row .bar-track{
  background:var(--paper);
  border:1px solid var(--ink);
}
.bar-budget{ background:var(--tray); }
.bar-budget.is-tight{ background:var(--safelight); }

/* the masthead sits on the film rebate, so atlas's vermillion
   hover lands at 2.72:1 against it. The on-field signal is the
   same gesture at 5.77:1. */
.masthead a:hover,.masthead .brand:hover{
  color:var(--on-field-signal); border-color:var(--on-field-signal);
}

/* ── per-file rows in a batch ────────────────────────────────*/
.file-row{
  display:grid;
  grid-template-columns:1fr auto auto;
  gap:.75rem; align-items:center;
  padding:.6rem 0;
  border-bottom:1px solid var(--rule-2);
}
.file-row:last-child{ border-bottom:0; }
.file-name{
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  font-size:.9375rem; color:var(--ink);
}
.file-size{ font-variant-numeric:tabular-nums lining-nums; color:var(--ink-2); font-size:.875rem; }

/* status carries text as well as colour — never colour alone */
.file-status{
  font-size:.6875rem; letter-spacing:.14em; text-transform:uppercase; font-weight:600;
}
.file-status[data-state="queued"]{ color:var(--ink-3); }
.file-status[data-state="working"]{ color:var(--ochre-ink); }
.file-status[data-state="done"]{ color:var(--olive-ink); }
.file-status[data-state="failed"]{ color:var(--chinagraph-ink); }

/* ── touch targets ───────────────────────────────────────────
   The generic 44px minimum now lives in shared/atlas.css, where it
   covers all nine sites. Only the rows unique to this cluster stay
   here. */
@media (max-width:60rem){
  .file-row a[download],.file-row a.btn,.file-row a.btn-quiet{
    display:inline-flex; align-items:center; min-height:44px;
  }
}

/* A file that is not what this page is about — stated, not blocked.

   No accent bar. The system's idiom for "this needs your attention" is
   .field-error: the colour lives in the TEXT, not in a rule beside it.
   The only left border atlas.css defines is .timeline's, in --ink, where
   it means the spine of a sequence. A 3px coloured bar here would be an
   invented decoration, and the rule hierarchy in DESIGN.md has no such
   weight — 1px --rule is what separates one thing from the next. */
.mismatch{
  border-top:1px solid var(--rule);
  padding-top:.5rem;
  margin-top:.6rem;
  color:var(--ochre-ink);      /* safelight as text — 4.71:1 on --paper-2 */
}
.mismatch a{ color:var(--chinagraph-ink); }

/* ── motion ──────────────────────────────────────────────────
   One authored moment, inherited: the plate redraws. Nothing here
   loops, and everything stops under reduced-motion. */
@media (prefers-reduced-motion:reduce){
  .drop{ transition:none; }
}
