/* ===========================================================================
   AlbumForge: contact sheet.

   Third world for this surface, and the first one that comes from the subject
   rather than from dashboard convention. A contact sheet is how a photographer
   looks at a roll: every frame the same size on one sheet of paper, film rebate
   printed along the edge, and a chinagraph pencil to ring the selects.

   That maps exactly onto the job here, so it is not decoration:
   - Frames are uniform. A contact sheet does not crop or resize to taste, and
     neither does this, so nothing is judged bigger than anything else.
   - Marks are the interaction. Ringing a frame is selection; crossing it out is
     rejection. Both are the pencil, both read across a room.
   - Edge printing carries the metadata. Album code, date, frame counts and
     state live in the rebate, the way stock and frame numbers do on film.

   Paper is the default. Negative mode inverts to film base and paper frames.

   Radius: this world is photographic paper and film, so almost nothing is
   rounded. Frames and panels are square (2px), controls 4px, the pencil marks
   are the only curves on the page.
=========================================================================== */

:root {
  color-scheme: light;

  --paper: #edeae4;          /* photographic paper, warm neutral, not cream */
  --paper-2: #e3dfd8;        /* sheet shadow, wells */
  --card: #f6f4f0;
  --film: #17161a;           /* rebate, frame surrounds */
  --film-2: #26242a;
  --ink: #17161a;
  --ink-dim: #6a6660;
  --rule: #c9c4bb;
  --rule-strong: #a29c92;

  --grease: #e4362a;         /* chinagraph pencil, marks and primary action */
  --grease-ink: #ffffff;
  --grease-wash: rgba(228, 54, 42, .12);

  --r-frame: 2px;
  --r-control: 4px;

  --ease: cubic-bezier(.2, .75, .3, 1);
  --fast: .15s var(--ease);
  --slow: .3s var(--ease);

  --sprocket-h: 30px;
}

/* Negative: the sheet inverted, for a dark room or a dark system setting. */
@media (prefers-color-scheme: dark) {
  :root {
    color-scheme: dark;
    --paper: #131215;
    --paper-2: #0b0a0c;
    --card: #1b1a1f;
    --film: #eceae5;
    --film-2: #d6d2ca;
    --ink: #eceae5;
    --ink-dim: #918c85;
    --rule: #2e2c33;
    --rule-strong: #474450;
    --grease: #ff5346;
    --grease-wash: rgba(255, 83, 70, .16);
  }
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }

body {
  background: var(--paper);
  color: var(--ink);
  font-family: "Geist", ui-sans-serif, system-ui, -apple-system, sans-serif;
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  min-height: 100dvh;
}

button, input { font: inherit; color: inherit; }
:focus-visible { outline: 2px solid var(--grease); outline-offset: 2px; }

.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

/* --- Edge printing --------------------------------------------------------
   Film edge text: mono, small, widely tracked, always uppercase. Every label,
   count and code on the page uses it. Prose is Geist and stays rare. */
.edge {
  font-family: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 10px; font-weight: 500;
  letter-spacing: .16em; text-transform: uppercase;
}
.mono { font-family: "Geist Mono", ui-monospace, monospace; font-variant-numeric: tabular-nums; }

h1, h2, h3 {
  font-family: "Archivo Narrow", "Archivo", ui-sans-serif, system-ui, sans-serif;
  font-weight: 700; text-transform: uppercase; letter-spacing: .04em; margin: 0;
}

/* --- Rebate ---------------------------------------------------------------
   The black strip with sprocket holes, top and bottom of the sheet. Holes are
   a repeating gradient, not images, so they scale to any width and cost
   nothing. */
.rebate {
  background: var(--film);
  color: var(--paper);
  position: relative;
}
.rebate::before,
.rebate::after {
  content: "";
  position: absolute; left: 0; right: 0; height: 9px;
  background-image: repeating-linear-gradient(
    to right,
    transparent 0 9px,
    var(--paper) 9px 20px
  );
  -webkit-mask-image: linear-gradient(#000, #000);
  opacity: .92;
}
.rebate::before { top: 5px; }
.rebate::after { bottom: 5px; }

.rebate--head {
  position: sticky; top: 0; z-index: 40;
  padding: calc(var(--sprocket-h) / 2 + 4px) clamp(12px, 3vw, 22px);
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
}
.rebate__stock { font-family: "Archivo Narrow", sans-serif; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; font-size: 13px; }
.rebate__meta { display: flex; align-items: center; gap: 14px; margin-left: auto; flex-wrap: wrap; }
.rebate .edge { color: color-mix(in srgb, var(--paper) 72%, transparent); }

/* State is printed in the rebate like a frame marking, not shown as a pill. */
.state { display: inline-flex; align-items: center; gap: 6px; }
.state__dot { width: 6px; height: 6px; border-radius: 50%; background: var(--paper); }
.state--closed .state__dot { background: var(--grease); }
.state--closed .state__text { color: var(--grease); }

/* --- Sheet ---------------------------------------------------------------- */
.sheet {
  max-width: 1160px; margin: 0 auto;
  padding: clamp(16px, 3vw, 28px) clamp(12px, 3vw, 24px) 90px;
}

.slate {
  display: grid; gap: 14px;
  padding-bottom: 14px; margin-bottom: 18px;
  border-bottom: 2px solid var(--ink);
}
@media (min-width: 720px) {
  .slate { grid-template-columns: minmax(0, 1fr) auto; align-items: end; gap: 26px; }
}
.slate__title { font-size: clamp(20px, 3.6vw, 30px); line-height: 1.05; overflow-wrap: anywhere; }
.slate__sub { margin-top: 6px; color: var(--ink-dim); }

.counters { display: flex; gap: clamp(16px, 4vw, 34px); }
.counter { display: grid; gap: 2px; }
.counter__n {
  font-family: "Geist Mono", ui-monospace, monospace; font-variant-numeric: tabular-nums;
  font-size: clamp(22px, 3.6vw, 30px); font-weight: 500; line-height: 1;
}
.counter__n--marked { color: var(--grease); }
.counter .edge { color: var(--ink-dim); }

/* --- Tabs: notches on the strip ------------------------------------------ */
.strip {
  display: flex; gap: 0; margin-bottom: 20px;
  border: 1px solid var(--ink);
  background: var(--card);
}
.notch {
  flex: 1; min-height: 48px; padding: 0 10px;
  border: 0; border-right: 1px solid var(--ink);
  background: transparent; color: var(--ink-dim);
  font-family: "Geist Mono", ui-monospace, monospace;
  font-size: 10.5px; letter-spacing: .14em; text-transform: uppercase;
  cursor: pointer; position: relative;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  transition: background var(--fast), color var(--fast);
}
.notch:last-child { border-right: 0; }
.notch:hover { background: var(--paper-2); color: var(--ink); }
.notch[aria-current="true"] { background: var(--ink); color: var(--paper); }
.notch__count {
  min-width: 18px; height: 18px; padding: 0 4px; border-radius: 9px;
  background: var(--grease); color: var(--grease-ink);
  display: grid; place-items: center; font-size: 10px;
}
.notch__count[hidden] { display: none; }

/* --- Frames ---------------------------------------------------------------
   Uniform cells. Images are contained on film, never cropped, because a contact
   sheet shows the whole frame. */
.frames {
  display: grid; gap: 12px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (min-width: 620px) { .frames { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 900px) { .frames { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (min-width: 1120px) { .frames { grid-template-columns: repeat(5, minmax(0, 1fr)); } }

.frame { display: grid; gap: 5px; cursor: pointer; }

.frame__window {
  position: relative;
  aspect-ratio: 3 / 2;
  background: var(--film);
  border-radius: var(--r-frame);
  overflow: hidden;
}
.frame__window img { width: 100%; height: 100%; object-fit: contain; display: block; }

.frame__foot { display: flex; align-items: baseline; gap: 6px; }
.frame__no {
  font-family: "Geist Mono", ui-monospace, monospace; font-size: 10px;
  letter-spacing: .1em; color: var(--ink-dim); flex: none;
}
.frame__who { font-size: 11px; color: var(--ink-dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.frame--marked .frame__no { color: var(--grease); }

.frame__held {
  position: absolute; top: 5px; left: 5px;
  padding: 2px 6px; border-radius: 2px;
  background: var(--grease); color: var(--grease-ink);
  font-family: "Geist Mono", ui-monospace, monospace; font-size: 9px; letter-spacing: .1em; text-transform: uppercase;
}
.frame__cover {
  position: absolute; top: 5px; right: 5px;
  padding: 2px 6px; border-radius: 2px;
  background: var(--paper); color: var(--ink);
  font-family: "Geist Mono", ui-monospace, monospace; font-size: 9px; letter-spacing: .1em; text-transform: uppercase;
}

/* Chinagraph marks. Deliberately imperfect: an off-round radius and a slight
   rotation, because a ring drawn with a wax pencil never closes cleanly. */
.mark {
  position: absolute; inset: 9% 7%;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--fast), transform var(--fast);
  transform: scale(.94);
}
.mark--ring {
  border: 3.5px solid var(--grease);
  border-radius: 49% 51% 52% 48% / 51% 49% 51% 49%;
  transform: rotate(-4deg) scale(.94);
}
.frame--marked .mark--ring { opacity: .92; transform: rotate(-4deg) scale(1); }

.mark--cross::before,
.mark--cross::after {
  content: ""; position: absolute; top: 50%; left: -2%; right: -2%;
  height: 3.5px; background: var(--grease); border-radius: 2px;
}
.mark--cross::before { transform: rotate(21deg); }
.mark--cross::after { transform: rotate(-21deg); }

.frame input { position: absolute; opacity: 0; pointer-events: none; }

/* --- Loupe ----------------------------------------------------------------
   One frame, enlarged, the way you would drop a loupe onto the sheet. */
.loupe { display: grid; gap: 14px; }
.loupe__head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.loupe__count { font-family: "Geist Mono", ui-monospace, monospace; font-size: 12px; letter-spacing: .1em; color: var(--ink-dim); }

.loupe__window {
  position: relative;
  background: var(--film);
  border-radius: var(--r-frame);
  overflow: hidden;
  aspect-ratio: 4 / 5;
  touch-action: pan-y;
}
@media (min-width: 720px) { .loupe__window { aspect-ratio: 3 / 2; } }
.loupe__window img {
  width: 100%; height: 100%; object-fit: contain; display: block;
  transition: transform var(--slow), opacity var(--slow);
}
.loupe__window.is-marking img { transform: translateX(22%) rotate(2.5deg); opacity: 0; }
.loupe__window.is-crossing img { transform: translateX(-22%) rotate(-2.5deg); opacity: 0; }

.loupe__mark {
  position: absolute; inset: 8%;
  opacity: 0; pointer-events: none;
  transition: opacity .12s var(--ease);
}
.loupe__window.is-marking .loupe__mark--ring,
.loupe__window.is-crossing .loupe__mark--cross { opacity: .95; }

.loupe__bar {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 30px 12px 10px;
  background: linear-gradient(transparent, rgba(10, 9, 11, .82));
  color: #eceae5;
  display: flex; justify-content: space-between; align-items: baseline; gap: 10px;
}
.loupe__actions { display: flex; gap: 10px; }
.loupe__hint { font-family: "Geist Mono", ui-monospace, monospace; font-size: 10px; letter-spacing: .12em; text-transform: uppercase; color: var(--ink-dim); text-align: center; }

/* --- Controls -------------------------------------------------------------
   Square, bordered, ink on paper. The pencil is the only fill on the page. */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 46px; padding: 0 16px;
  border: 1px solid var(--ink); border-radius: var(--r-control);
  background: transparent; color: var(--ink);
  font-family: "Geist Mono", ui-monospace, monospace;
  font-size: 11.5px; letter-spacing: .12em; text-transform: uppercase;
  cursor: pointer; white-space: nowrap;
  transition: background var(--fast), color var(--fast), transform var(--fast);
}
.btn:hover { background: var(--ink); color: var(--paper); }
.btn:active { transform: translateY(1px); }
.btn[disabled] { opacity: .38; cursor: not-allowed; }
.btn[disabled]:hover { background: transparent; color: var(--ink); }

.btn--grease { background: var(--grease); border-color: var(--grease); color: var(--grease-ink); }
.btn--grease:hover { background: var(--grease); border-color: var(--grease); color: var(--grease-ink); filter: brightness(1.06); }

.btn--ghost { border-color: var(--rule-strong); color: var(--ink-dim); }
.btn--ghost:hover { background: transparent; border-color: var(--ink); color: var(--ink); }

.btn--big { min-height: 54px; flex: 1; font-size: 12.5px; }
.btn--small { min-height: 36px; padding: 0 11px; font-size: 10.5px; }
.btn--block { width: 100%; }
.btn--danger { border-color: var(--rule-strong); color: var(--grease); }
.btn--danger:hover { background: var(--grease-wash); border-color: var(--grease); color: var(--grease); }

/* --- Marked bar ----------------------------------------------------------- */
.marked-bar {
  position: fixed; z-index: 45; left: 10px; right: 10px; bottom: 10px;
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 9px 11px;
  border: 1px solid var(--ink);
  background: var(--card);
  box-shadow: 0 14px 30px -18px rgba(20, 18, 16, .7);
}
@media (min-width: 900px) { .marked-bar { left: 50%; right: auto; transform: translateX(-50%); min-width: 560px; } }
.marked-bar[hidden] { display: none; }
.marked-bar__n { margin-right: auto; font-family: "Geist Mono", ui-monospace, monospace; font-size: 11px; letter-spacing: .1em; text-transform: uppercase; }

/* --- Setup rows ----------------------------------------------------------- */
.plate { display: grid; gap: 26px; grid-template-columns: minmax(0, 1fr); align-items: start; }
@media (min-width: 900px) { .plate { grid-template-columns: minmax(0, 1fr) 300px; } }

.block { margin-bottom: 26px; }
.block__title { font-size: 13px; margin-bottom: 10px; }
.block__note { margin: 0 0 12px; color: var(--ink-dim); font-size: 13px; max-width: 58ch; }

.row { display: flex; align-items: center; gap: 14px; padding: 13px 0; border-top: 1px solid var(--rule); }
.row:last-child { border-bottom: 1px solid var(--rule); }
.row__main { flex: 1; min-width: 0; }
.row__label { font-size: 14px; }
.row__value { color: var(--ink-dim); font-size: 12px; margin-top: 3px; overflow-wrap: anywhere; }

.toggle {
  position: relative; flex: none; width: 52px; height: 26px;
  border: 1px solid var(--ink); border-radius: var(--r-control);
  background: transparent; cursor: pointer; transition: background var(--fast);
}
.toggle::after {
  content: ""; position: absolute; top: 3px; left: 3px;
  width: 18px; height: 18px; border-radius: 2px;
  background: var(--rule-strong); transition: transform var(--fast), background var(--fast);
}
.toggle[aria-checked="true"] { background: var(--grease-wash); border-color: var(--grease); }
.toggle[aria-checked="true"]::after { transform: translateX(24px); background: var(--grease); }

.field { display: grid; gap: 6px; margin-bottom: 12px; }
.field label { font-size: 12px; color: var(--ink-dim); }
.field input {
  min-height: 46px; padding: 0 12px;
  border: 1px solid var(--rule-strong); border-radius: var(--r-control);
  background: var(--card); color: var(--ink);
}
.field input:focus { outline: none; border-color: var(--grease); }
.field .err { font-size: 12px; color: var(--grease); }

.danger-zone { margin-top: 22px; padding: 15px; border: 1px solid var(--rule-strong); border-left: 3px solid var(--grease); }
.danger-zone__actions { display: flex; gap: 9px; flex-wrap: wrap; margin-top: 11px; }

/* --- Card ----------------------------------------------------------------- */
.card { border: 1px solid var(--ink); background: var(--card); max-width: 300px; }
.card__body { padding: 16px; display: grid; gap: 10px; justify-items: center; text-align: center; }
.card__qr { background: #fff; padding: 9px; width: 100%; }
.card__qr img, .card__qr svg { display: block; width: 100%; height: auto; }
.card__stub { border-top: 1px dashed var(--rule-strong); padding: 10px 16px; display: flex; justify-content: space-between; gap: 8px; }

/* --- Feedback ------------------------------------------------------------- */
.toast {
  position: fixed; z-index: 60; left: 50%; bottom: 74px; transform: translate(-50%, 8px);
  padding: 9px 15px; background: var(--ink); color: var(--paper);
  font-family: "Geist Mono", ui-monospace, monospace; font-size: 11px;
  letter-spacing: .12em; text-transform: uppercase;
  opacity: 0; pointer-events: none;
  transition: opacity var(--fast), transform var(--fast);
}
.toast.is-visible { opacity: 1; transform: translate(-50%, 0); }

.blank { border: 1px dashed var(--rule-strong); padding: clamp(28px, 6vw, 48px) 20px; text-align: center; display: grid; gap: 8px; justify-items: center; }
.blank h3 { font-size: 15px; }
.blank p { margin: 0; color: var(--ink-dim); font-size: 13px; max-width: 40ch; }

.latent { background: var(--paper-2); position: relative; overflow: hidden; }
.latent::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(100deg, transparent 30%, rgba(255,255,255,.35) 50%, transparent 70%);
  animation: develop 1.4s linear infinite;
}
@keyframes develop { from { transform: translateX(-60%); } to { transform: translateX(60%); } }

.signin { max-width: 320px; margin: clamp(40px, 14vh, 110px) auto 0; }
.signin h1 { font-size: 22px; margin-bottom: 6px; }
.signin p { margin: 0 0 18px; color: var(--ink-dim); font-size: 13px; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}

@media print {
  .rebate--head, .strip, .marked-bar, .toast, .plate > *:not(.card-rail) { display: none !important; }
  body { background: #fff; color: #000; }
  .sheet { padding: 0; }
  .card { border: 1px solid #999; max-width: 340px; margin: 0 auto; }
}


/* --- Theme chooser --------------------------------------------------------
   Swatches rather than a select, because the choice is visual. Each chip shows
   the theme's paper, ink and accent in the proportions the album will use. */
.themes { display: flex; gap: 9px; flex-wrap: wrap; }

.theme-chip {
  border: 1px solid var(--rule-strong); border-radius: var(--r-control);
  background: transparent; padding: 0; cursor: pointer;
  width: 92px; overflow: hidden; text-align: left;
  transition: border-color var(--fast), transform var(--fast);
}
.theme-chip:hover { border-color: var(--ink); }
.theme-chip:active { transform: translateY(1px); }
.theme-chip[aria-checked="true"] { border-color: var(--grease); box-shadow: inset 0 0 0 1px var(--grease); }

.theme-chip__swatch { height: 46px; position: relative; display: block; }
.theme-chip__swatch::after {
  content: ""; position: absolute; left: 8px; bottom: 8px;
  width: 16px; height: 4px; border-radius: 2px; background: var(--chip-accent);
}
.theme-chip__rule {
  position: absolute; left: 8px; top: 10px; right: 8px; height: 3px;
  background: var(--chip-ink); opacity: .85; border-radius: 2px;
}
.theme-chip__rule + .theme-chip__rule { top: 18px; right: 26px; opacity: .5; }

.theme-chip__name {
  display: block; padding: 6px 8px 7px;
  border-top: 1px solid var(--rule);
  font-family: "Geist Mono", ui-monospace, monospace;
  font-size: 9.5px; letter-spacing: .12em; text-transform: uppercase;
  color: var(--ink-dim);
}
.theme-chip[aria-checked="true"] .theme-chip__name { color: var(--ink); }
