/* =============================================================================
   image.noxelstudio.com — feuille unique
   -----------------------------------------------------------------------------
   Meme famille que noxelstudio.com et send.noxelstudio.com : encre sur papier,
   Space Grotesk pour le texte, DM Mono en capitales espacees pour tout ce qui
   se lit comme une donnee ou se clique. Un seul accent, le rouge.

   Une difference assumee avec Send : le grain de pellicule reste SOUS le
   contenu. Ici les images sont le sujet, et un voile de bruit par-dessus une
   photo qu'on est en train de recadrer ment sur ce qui sera exporte.

   L'outil se tient en une page qui ne defile pas : barre, panneau de reglages a
   gauche, planche de vignettes a droite. Seule la planche defile.
   ============================================================================= */

/* ---------- polices, hebergees ici ---------- */
@font-face { font-family:'DM Mono'; font-style:normal; font-weight:400; font-display:swap;
  src:url('/fonts/dm-mono-400-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face { font-family:'DM Mono'; font-style:normal; font-weight:400; font-display:swap;
  src:url('/fonts/dm-mono-400-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }
@font-face { font-family:'DM Mono'; font-style:normal; font-weight:500; font-display:swap;
  src:url('/fonts/dm-mono-500-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face { font-family:'DM Mono'; font-style:normal; font-weight:500; font-display:swap;
  src:url('/fonts/dm-mono-500-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }
@font-face { font-family:'Space Grotesk'; font-style:normal; font-weight:400; font-display:swap;
  src:url('/fonts/space-grotesk-400-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face { font-family:'Space Grotesk'; font-style:normal; font-weight:400; font-display:swap;
  src:url('/fonts/space-grotesk-400-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }
@font-face { font-family:'Space Grotesk'; font-style:normal; font-weight:500; font-display:swap;
  src:url('/fonts/space-grotesk-500-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face { font-family:'Space Grotesk'; font-style:normal; font-weight:500; font-display:swap;
  src:url('/fonts/space-grotesk-500-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }
@font-face { font-family:'Space Grotesk'; font-style:normal; font-weight:700; font-display:swap;
  src:url('/fonts/space-grotesk-700-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face { font-family:'Space Grotesk'; font-style:normal; font-weight:700; font-display:swap;
  src:url('/fonts/space-grotesk-700-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF; }

/* ---------- variables ---------- */
:root {
  --paper:#f3f0ea;
  --paper-2:#ece8e1;
  --wash:#fbf9f4;
  --ink:#141210;
  --ink-2:rgba(20,18,16,.62);
  --ink-3:rgba(20,18,16,.38);
  --rule:rgba(20,18,16,.16);
  --rule-2:rgba(20,18,16,.08);
  --field:rgba(20,18,16,.035);
  --acc:#c81d10;
  --acc-ink:#fff;
  --shadow:0 1px 2px rgba(20,18,16,.05), 0 14px 38px -20px rgba(20,18,16,.38);
  --grain-o:.05;

  --sans:'Space Grotesk',ui-sans-serif,system-ui,-apple-system,'Segoe UI',sans-serif;
  --mono:'DM Mono',ui-monospace,'SF Mono',Menlo,monospace;

  --bar:56px;
  --aside:290px;
  --r:3px;
  --r-lg:10px;
  --ease:cubic-bezier(.2,.7,.3,1);
  --pop:cubic-bezier(.16,1.1,.3,1);
}

@media (prefers-color-scheme:dark) {
  :root {
    --paper:#0f0e0d;
    --paper-2:#171513;
    --wash:#1b1815;
    --ink:#ede9e2;
    --ink-2:rgba(237,233,226,.62);
    --ink-3:rgba(237,233,226,.36);
    --rule:rgba(237,233,226,.17);
    --rule-2:rgba(237,233,226,.08);
    --field:rgba(237,233,226,.045);
    --acc:#ff4a38;
    --acc-ink:#14100f;
    --shadow:0 1px 2px rgba(0,0,0,.45), 0 20px 48px -24px rgba(0,0,0,.95);
    --grain-o:.09;
  }
}

/* ---------- socle ---------- */
*, *::before, *::after { box-sizing:border-box; }
html, body { height:100%; }
body {
  margin:0; background:var(--paper); color:var(--ink);
  font:400 15px/1.5 var(--sans);
  -webkit-font-smoothing:antialiased;
  overflow:hidden;                     /* seule la planche defile */
  overscroll-behavior:none;
}
.grain {
  position:fixed; inset:0; z-index:0; pointer-events:none; opacity:var(--grain-o);
  background-image: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%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
::selection { background:var(--acc); color:var(--acc-ink); }
:focus-visible { outline:2px solid var(--acc); outline-offset:2px; }
.spacer { flex:1 1 auto; }
.lbl {
  font:500 10.5px/1.4 var(--mono); letter-spacing:.16em; text-transform:uppercase;
  color:var(--ink-3); margin:0;
}
.mono { font-family:var(--mono); font-variant-numeric:tabular-nums; }
.note { margin:0; font-size:12.5px; line-height:1.5; color:var(--ink-2); }
.big { font:700 clamp(19px,2.4vw,25px)/1.2 var(--sans); letter-spacing:-.03em; margin:0; }

/* ---------- barre ---------- */
.bar {
  position:relative; z-index:3;
  display:flex; align-items:center; gap:.7rem;
  height:var(--bar); padding:0 1rem 0 1.15rem;
  border-bottom:1px solid var(--rule-2); background:var(--paper);
}
.bar::before { content:''; position:absolute; inset:0 0 auto 0; height:2px; background:var(--acc); }
.mark { font:700 17px/1 var(--sans); letter-spacing:-.03em; margin:0; }

/* ---------- boutons ---------- */
.btn {
  appearance:none; border:1px solid var(--rule); background:var(--wash); color:var(--ink);
  font:500 10.5px/1 var(--mono); letter-spacing:.14em; text-transform:uppercase;
  padding:.62rem .8rem; border-radius:var(--r); cursor:pointer;
  transition:background .15s var(--ease), border-color .15s var(--ease), transform .1s var(--ease);
}
.btn:active { transform:translateY(1px); }
.btn.ghost { background:transparent; border-color:transparent; color:var(--ink-2); }
.btn[disabled] { opacity:.4; cursor:not-allowed; }
@media (hover:hover) {
  .btn:hover:not([disabled]) { background:var(--paper-2); border-color:var(--ink-3); }
  .btn.ghost:hover:not([disabled]) { color:var(--ink); background:var(--field); }
}

/* ---------- charpente ---------- */
.shell { position:relative; z-index:2; display:flex; height:calc(100% - var(--bar)); }

.panel {
  flex:0 0 var(--aside); width:var(--aside);
  display:flex; flex-direction:column; gap:1.35rem;
  padding:1.25rem 1.15rem 1.15rem;
  border-right:1px solid var(--rule-2); background:var(--paper);
  overflow:auto;
}
.block { display:flex; flex-direction:column; gap:.6rem; }

/* ---------- cadres ---------- */
.ratios { display:grid; grid-template-columns:repeat(3,1fr); gap:.35rem; }
.ratio {
  appearance:none; cursor:pointer; background:var(--wash);
  border:1px solid var(--rule-2); border-radius:var(--r); color:var(--ink-2);
  padding:.5rem .2rem .45rem; display:grid; gap:.34rem; justify-items:center;
  transition:border-color .15s var(--ease), color .15s var(--ease), background .15s var(--ease);
}
.ratio i {
  display:block; border:1.5px solid currentColor; border-radius:1px;
  width:var(--w); height:var(--h); opacity:.75;
}
.ratio span { font:500 9.5px/1 var(--mono); letter-spacing:.1em; }
.ratio.on { color:var(--ink); border-color:var(--ink); background:var(--paper-2); }
.ratio.on i { opacity:1; }
@media (hover:hover) { .ratio:hover { color:var(--ink); border-color:var(--rule); } }

/* ---------- champs ---------- */
.size { display:flex; align-items:flex-end; gap:.45rem; }
.field { flex:1 1 0; display:flex; flex-direction:column; gap:.3rem; min-width:0; }
.times { padding-bottom:.62rem; color:var(--ink-3); font:400 13px var(--mono); }
.num {
  width:100%; appearance:none; -moz-appearance:textfield;
  border:1px solid var(--rule); background:var(--field); color:var(--ink);
  border-radius:var(--r); padding:.55rem .55rem;
  font:500 15px/1 var(--mono); font-variant-numeric:tabular-nums;
}
.num:disabled { opacity:.45; }

/* ---------- unites, inversion, formats ---------- */
.row { display:flex; align-items:center; gap:.5rem; }
.row .lbl { flex:1 1 auto; }

.units { display:flex; gap:2px; }
.unit {
  appearance:none; cursor:pointer; padding:.3rem .45rem;
  border:1px solid var(--rule-2); background:var(--wash); color:var(--ink-3);
  border-radius:var(--r); font:500 9.5px/1 var(--mono); letter-spacing:.1em;
  transition:color .15s var(--ease), border-color .15s var(--ease);
}
.unit.on { color:var(--ink); border-color:var(--ink); background:var(--paper-2); }
@media (hover:hover) { .unit:hover { color:var(--ink); } }

.swap {
  appearance:none; cursor:pointer; flex:0 0 auto;
  width:38px; height:38px; display:grid; place-items:center;
  border:1px solid var(--rule); background:var(--wash); color:var(--ink-2);
  border-radius:var(--r); font-size:15px; line-height:1;
  transition:color .15s var(--ease), border-color .15s var(--ease), background .15s var(--ease);
}
@media (hover:hover) { .swap:hover { color:var(--ink); border-color:var(--ink-3); background:var(--paper-2); } }

.dpi[hidden] { display:none; }
.suffixed { position:relative; display:block; }
.suffixed i {
  position:absolute; right:.6rem; top:50%; transform:translateY(-50%);
  font:500 9.5px/1 var(--mono); letter-spacing:.1em; color:var(--ink-3); font-style:normal;
  pointer-events:none;
}
.suffixed .num { padding-right:2.4rem; }

.presets { display:grid; grid-template-columns:repeat(4,1fr); gap:.25rem; }
.preset {
  appearance:none; cursor:pointer; padding:.45rem .1rem;
  border:1px solid var(--rule-2); background:var(--wash); color:var(--ink-2);
  border-radius:var(--r); font:500 9.5px/1 var(--mono); letter-spacing:.06em;
  transition:color .15s var(--ease), border-color .15s var(--ease), background .15s var(--ease);
}
.preset.on { color:var(--ink); border-color:var(--ink); background:var(--paper-2); }
@media (hover:hover) { .preset:hover { color:var(--ink); border-color:var(--rule); } }

.segmented { display:grid; grid-template-columns:repeat(4,1fr); gap:.25rem; }
.seg {
  appearance:none; cursor:pointer; padding:.55rem .1rem;
  border:1px solid var(--rule-2); background:var(--wash); color:var(--ink-2);
  border-radius:var(--r); font:500 9.5px/1 var(--mono); letter-spacing:.1em; text-transform:uppercase;
  transition:border-color .15s var(--ease), color .15s var(--ease), background .15s var(--ease);
}
.seg.on { color:var(--ink); border-color:var(--ink); background:var(--paper-2); }
@media (hover:hover) { .seg:hover { color:var(--ink); } }

.quality { display:flex; flex-direction:column; gap:.45rem; }
.quality b { color:var(--ink-2); font-weight:500; letter-spacing:0; }
.quality[hidden] { display:none; }
input[type=range] {
  appearance:none; width:100%; height:2px; background:var(--rule); border-radius:2px; margin:.5rem 0;
}
input[type=range]::-webkit-slider-thumb {
  appearance:none; width:15px; height:15px; border-radius:50%;
  background:var(--ink); border:2px solid var(--paper); cursor:pointer;
}
input[type=range]::-moz-range-thumb {
  width:13px; height:13px; border-radius:50%; background:var(--ink);
  border:2px solid var(--paper); cursor:pointer;
}

/* ---------- exporter ---------- */
.go { gap:.7rem; }
.go-btn {
  position:relative; overflow:hidden; padding:.95rem .8rem;
  background:var(--acc); border-color:var(--acc); color:var(--acc-ink); font-size:11.5px;
}
.go-btn[disabled] { background:var(--field); border-color:var(--rule-2); color:var(--ink-3); opacity:1; }
@media (hover:hover) { .go-btn:hover:not([disabled]) { background:var(--acc); border-color:var(--ink); } }
.go-fill {
  position:absolute; inset:0 auto 0 0; width:0; background:rgba(0,0,0,.28);
  transition:width .18s linear;
}
.go-text { position:relative; }

/* ---------- planche ---------- */
.sheet { flex:1 1 auto; overflow:auto; padding:1.15rem; background:var(--paper-2); position:relative; }
.grid {
  display:grid; gap:.85rem;
  grid-template-columns:repeat(auto-fill, minmax(var(--cell,190px), 1fr));
}
.card {
  display:flex; flex-direction:column; gap:.4rem;
  animation:rise .3s var(--pop) both;
  /* Trois cents vignettes : le navigateur ne met en page que celles qu'on voit. */
  content-visibility:auto; contain-intrinsic-size:auto 240px;
}
@keyframes rise { from { opacity:0; transform:translateY(6px); } to { opacity:1; transform:none; } }

.frame {
  position:relative; overflow:hidden; aspect-ratio:var(--ar,1);
  background:var(--wash); border:1px solid var(--rule-2); border-radius:var(--r-lg);
  cursor:grab; user-select:none;
  /* Le doigt fait defiler la planche ; le recadrage au toucher se fait dans la
     loupe, ou aucun autre geste ne le dispute. */
  touch-action:pan-y;
  transition:border-color .15s var(--ease);
}
.frame.dragging { cursor:grabbing; border-color:var(--ink); }
/* Rien a deplacer : l'image remplit deja son cadre. */
.frame.fige { cursor:default; }

/* Ce qu'on voit derriere l'image est ce que l'export mettra dans les marges :
   du blanc pour un JPEG, de la transparence pour les autres. Invisible tant
   que l'image remplit le cadre — c'est en dezoomant que ca compte. */
.frame.opaque { background:#fff; }
.frame.alpha {
  background-color:var(--paper-2);
  background-image:
    linear-gradient(45deg, var(--rule-2) 25%, transparent 25%, transparent 75%, var(--rule-2) 75%),
    linear-gradient(45deg, var(--rule-2) 25%, transparent 25%, transparent 75%, var(--rule-2) 75%);
  background-size:14px 14px;
  background-position:0 0, 7px 7px;
}
.frame img {
  position:absolute; left:50%; top:50%; transform:translate(-50%,-50%);
  max-width:none; display:block; pointer-events:none; -webkit-user-drag:none;
}
.frame.pending { background:var(--field); }
.frame.failed { border-color:var(--acc); display:grid; place-items:center; cursor:default; }
.frame.failed::after {
  content:'illisible'; font:500 9.5px var(--mono); letter-spacing:.14em;
  text-transform:uppercase; color:var(--acc);
}
@media (hover:hover) { .frame:hover { border-color:var(--ink-3); } }

.kill {
  position:absolute; top:.4rem; right:.4rem; z-index:2;
  width:24px; height:24px; display:grid; place-items:center;
  border:0; border-radius:50%; cursor:pointer; padding:0;
  background:rgba(20,18,16,.62); color:#fff; font:400 15px/1 var(--sans);
  opacity:0; transition:opacity .15s var(--ease);
}
.card:hover .kill, .kill:focus-visible { opacity:1; }
@media (hover:none) { .kill { opacity:1; } }

.meta { display:flex; align-items:baseline; gap:.4rem; padding:0 .15rem; min-width:0; }
.meta .name {
  flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  font-size:12px; color:var(--ink-2);
}
.meta .dim { font:400 10.5px var(--mono); color:var(--ink-3); font-variant-numeric:tabular-nums; }
.meta .up { color:var(--acc); }

/* ---------- planche vide ---------- */
.empty {
  position:absolute; inset:1.15rem; display:grid; place-content:center; justify-items:center;
  gap:.7rem; text-align:center; padding:2rem;
  border:1px dashed var(--rule); border-radius:var(--r-lg);
}
.empty .note { max-width:38ch; }
.empty[hidden] { display:none; }
.marks { position:absolute; inset:14px; pointer-events:none; }
.marks i { position:absolute; width:16px; height:16px; border:1px solid var(--ink-3); }
.marks i:nth-child(1) { top:0; left:0; border-right:0; border-bottom:0; }
.marks i:nth-child(2) { top:0; right:0; border-left:0; border-bottom:0; }
.marks i:nth-child(3) { bottom:0; left:0; border-right:0; border-top:0; }
.marks i:nth-child(4) { bottom:0; right:0; border-left:0; border-top:0; }

/* ---------- rideau de depot ---------- */
.curtain {
  position:fixed; inset:0; z-index:40; display:grid; place-items:center;
  background:color-mix(in srgb, var(--paper) 88%, transparent);
  border:2px solid var(--acc); pointer-events:none;
}
.curtain[hidden] { display:none; }

/* ---------- loupe ---------- */
.loupe {
  position:fixed; inset:0; z-index:50; display:flex; flex-direction:column;
  background:color-mix(in srgb, var(--paper) 94%, transparent);
  animation:fade .18s var(--ease) both;
}
.loupe[hidden] { display:none; }
@keyframes fade { from { opacity:0; } to { opacity:1; } }
.loupe-stage {
  flex:1 1 auto; display:grid; place-items:center; padding:1.4rem; min-height:0;
}
.loupe-stage .frame {
  border-radius:var(--r-lg); box-shadow:var(--shadow);
  /* Une largeur explicite : l'image du cadre est en position absolue, donc un
     `width:auto` mesurerait zero. La hauteur suit par `aspect-ratio`. */
  width:min(calc(100vw - 3rem), calc((100dvh - 150px) * var(--ar,1)));
  height:auto; touch-action:none;
}
.loupe-stage .frame::after {
  content:''; position:absolute; inset:0; pointer-events:none;
  background:
    linear-gradient(to right, transparent 33.2%, var(--rule) 33.2%, var(--rule) 33.4%, transparent 33.4%,
      transparent 66.4%, var(--rule) 66.4%, var(--rule) 66.6%, transparent 66.6%),
    linear-gradient(to bottom, transparent 33.2%, var(--rule) 33.2%, var(--rule) 33.4%, transparent 33.4%,
      transparent 66.4%, var(--rule) 66.4%, var(--rule) 66.6%, transparent 66.6%);
  opacity:0; transition:opacity .15s var(--ease);
}
.loupe-stage .frame.dragging::after { opacity:1; }
.loupe-bar {
  display:flex; align-items:center; gap:.6rem; padding:.8rem 1rem;
  border-top:1px solid var(--rule-2); background:var(--paper);
}
.loupe-bar .lbl { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:40vw; }
.loupe-bar .hint { color:var(--ink-3); letter-spacing:.1em; }
@media (max-width:720px) { .loupe-bar .hint { display:none; } }

/* ---------- pastille ---------- */
.toast {
  position:fixed; left:50%; bottom:1.4rem; transform:translateX(-50%); z-index:60;
  padding:.7rem 1rem; border-radius:var(--r); border:1px solid var(--rule);
  background:var(--paper); box-shadow:var(--shadow);
  font:500 11px var(--mono); letter-spacing:.1em; text-transform:uppercase;
  animation:rise .25s var(--pop) both;
}
.toast[hidden] { display:none; }

/* ---------- ecrans etroits ---------- */
@media (max-width:820px) {
  body { overflow:auto; }
  .shell { flex-direction:column; height:auto; }
  .panel {
    flex:0 0 auto; width:100%; border-right:0; border-bottom:1px solid var(--rule-2);
    position:sticky; top:0; z-index:3; overflow:visible;
  }
  .sheet { overflow:visible; }
  .grid { --cell:140px; }
  .empty { position:static; }
}
@media (pointer:coarse) {
  .btn { padding:.75rem .85rem; }
  .num { font-size:16px; }
}
