/* Gear Room, in Tickmark's design: a technical datasheet. Monospace, small
   tracked uppercase labels, 1px rules; no shadows, rounded cards or
   gradients. The gear floats in rows over graph paper. */

@font-face {
  font-family: 'IBM Plex Mono';
  font-weight: 400;
  font-display: swap;
  src: url('fonts/IBMPlexMono-400.woff2') format('woff2');
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-weight: 600;
  font-display: swap;
  src: url('fonts/IBMPlexMono-600.woff2') format('woff2');
}
@font-face {
  font-family: 'IBM Plex Mono';
  font-weight: 700;
  font-display: swap;
  src: url('fonts/IBMPlexMono-700.woff2') format('woff2');
}
@font-face {
  font-family: 'IBM Plex Sans Condensed';
  font-weight: 700;
  font-display: swap;
  src: url('fonts/IBMPlexSansCondensed-700.woff2') format('woff2');
}

:root {
  --night: #0f0f0e;
  --lit: #ecebe4;
  --accent: #ff5a1f;
  --bg: var(--night);
  --fg: var(--lit);
  --mute: #9c9b92;
  --hair: #3a3a36;
  --rule: #5c5b55;
  --gridline: #262623;
  --mono: 'IBM Plex Mono', ui-monospace, 'SF Mono', Menlo, monospace;
  --cond: 'IBM Plex Sans Condensed', 'Arial Narrow', sans-serif;
  --safe-b: max(18px, env(safe-area-inset-bottom));
}

* {
  box-sizing: border-box;
}

/* `hidden` always hides, whatever display a class gives the element. */
[hidden] {
  display: none !important;
}

html,
body {
  height: 100%;
  margin: 0;
  /* The page never scrolls or bounces; only the shelves do. */
  overflow: hidden;
  overscroll-behavior: none;
  /* iOS tints the status bar area from the page background. */
  background: var(--night);
}

body {
  font: 12px/1.35 var(--mono);
  -webkit-tap-highlight-color: transparent;
  -webkit-text-size-adjust: 100%;
  -webkit-user-select: none;
  user-select: none;
}

button {
  margin: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  font: inherit;
  color: inherit;
  cursor: pointer;
}

.screen {
  height: 100%;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border-top: env(safe-area-inset-top) solid var(--night);
  background: var(--bg);
  color: var(--fg);
}

.fill {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* ---- Bars ------------------------------------------------------------------ */

.bar {
  flex: none;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 12px;
  border-bottom: 1px solid var(--rule);
}

.bar-title {
  flex: 1;
  display: flex;
  align-items: baseline;
  gap: 10px;
  min-width: 0;
}

.bar-title b {
  letter-spacing: 0.04em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.bar-title span,
.bar-note {
  flex: none;
  font-size: 11px;
  color: var(--mute);
  letter-spacing: 0.04em;
}

.bar-btn {
  height: 43px;
  padding: 0 12px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.04em;
}

/* Add sits flush right, boxed off like back on the left. */
.add-btn {
  flex: none;
  width: 48px;
  margin: 0 -12px 0 12px;
  padding: 0;
  border-left: 1px solid var(--rule);
  font-size: 20px;
  font-weight: 400;
}

/* Back sits flush left, boxed off from the title. */
.back {
  flex: none;
  margin: 0 12px 0 -12px;
  border-right: 1px solid var(--rule);
}

.label {
  font-size: 10px;
  letter-spacing: 0.06em;
  color: var(--mute);
}

.footer {
  flex: none;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 12px 12px var(--safe-b);
  border-top: 1px solid var(--rule);
}

.footer .label:empty {
  display: none;
}

.footer-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  min-height: 16px;
}
.footer-row:not(:has(.label:not(:empty), button:not([hidden]))) {
  display: none;
}

/* The add screen's button, under the form's column. */
#pick {
  width: 100%;
  max-width: 560px;
  align-self: center;
}

/* The add screen's buttons. */
.footer .buttons .big-btn {
  height: 64px;
  font-size: 13px;
}

.buttons {
  display: flex;
  gap: 8px;
}

/* Tickmark's big buttons: outlined, or filled with the accent. */
.big-btn {
  flex: 1;
  min-width: 0;
  height: 52px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  box-shadow: inset 0 0 0 1.5px var(--fg);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.08em;
  white-space: nowrap;
  cursor: pointer;
}

.big-btn.primary {
  flex: 1.7;
  background: var(--accent);
  color: #121212;
  box-shadow: none;
}

.big-btn[aria-disabled='true'] {
  opacity: 0.35;
  pointer-events: none;
}

.big-btn:active {
  transform: scale(0.97);
}

/* A scan on its way: a line of status over its progress, or why it failed
   with a button to clear it. */
.scan-status {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 6px 8px;
}
.scan-status .label {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.scan-status .progress {
  grid-column: 1 / -1;
}
.scan-status.failed .label {
  color: var(--accent);
}
.scan-status .clear {
  height: 28px;
  padding: 0 10px;
  font-size: 10px;
  box-shadow: inset 0 0 0 1px var(--accent);
  color: var(--accent);
}

/* An upload's or scan's progress: a filled bar in a 1px box. */
.progress {
  height: 8px;
  box-shadow: inset 0 0 0 1px var(--rule);
}
.progress span {
  display: block;
  height: 100%;
  width: 0;
  background: var(--fg);
}

/* ---- Shelves ----------------------------------------------------------------- */

main {
  position: relative;
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

/* Graph paper: the datasheet's take on a pegboard. */
.closet,
.viewer {
  background:
    linear-gradient(var(--gridline) 1px, transparent 1px) 0 0 / 24px 24px,
    linear-gradient(90deg, var(--gridline) 1px, transparent 1px) 0 0 / 24px 24px,
    var(--bg);
  background-attachment: local;
}

#shelves {
  /* app.js sets its height and places each piece (arrange.js). */
  position: relative;
  min-height: 100%;
}

.piece {
  position: absolute;
  left: 0;
  top: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  /* Scrolling still works from a piece; holding one picks it up. */
  touch-action: pan-y;
  -webkit-touch-callout: none;
  transition: transform 0.25s cubic-bezier(0.2, 0.8, 0.2, 1);
}

/* Picked up: follows the finger exactly, raised a little above the rest. */
.piece.lifted {
  z-index: 2;
  transition: none;
}
.piece.lifted .spin {
  transform: scale(1.1);
  outline: 1px dashed var(--accent);
  outline-offset: 6px;
}
.piece .spin {
  transition: transform 0.15s;
}

/* A turntable: two frames of the sprite sheet stacked, the second fading
   in as the turn reaches it (app.js). */
.spin {
  position: relative;
  flex: none;
}
.spin > div,
.flying {
  background-repeat: no-repeat;
}
.spin > div {
  position: absolute;
  inset: 0;
}



/* ---- Viewer ------------------------------------------------------------------ */

.viewer {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 0.3s;
}
.viewer.open {
  opacity: 1;
}
/* Kept laid out while closed so the canvas has a size to open at. */
.viewer[hidden] {
  display: block !important;
  visibility: hidden;
}

#splat {
  display: block;
  width: 100%;
  height: 100%;
  touch-action: none;
}

/* ---- Callout ---------------------------------------------------------------- */

/* Over the viewer's canvas; only the box takes taps, so dragging anywhere
   else still spins the gear. */
.callout {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.callout-line {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
}
.callout-line line {
  stroke: var(--fg);
  stroke-width: 1;
  /* Drawn out from the dot when the callout opens. */
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  transition: stroke-dashoffset 0.3s ease-out 0.15s;
}
.callout-dot {
  position: absolute;
  left: 0;
  top: 0;
  width: 9px;
  height: 9px;
  margin: -4.5px 0 0 -4.5px;
  padding: 0;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 3px rgb(255 90 31 / 0.25);
  /* Tapping it opens a folded callout. */
  pointer-events: auto;
  /* callout.js moves it onto the gear and grows it in. */
}
/* Nothing of the callout shows until callout.js has put it on the gear, so
   the dot never sits where the last piece's was. */
.callout:not(.shown) .callout-dot {
  visibility: hidden;
}

/* A bigger target than the dot itself. */
.callout-dot::after {
  content: '';
  position: absolute;
  inset: -14px;
  border-radius: 50%;
}
/* Folded, the dot pulses so it reads as something to tap. */
.callout.folded .callout-dot {
  animation: dot-pulse 1.6s ease-in-out infinite;
}
@keyframes dot-pulse {
  50% {
    box-shadow: 0 0 0 7px rgb(255 90 31 / 0.15);
  }
}
.callout-box {
  position: absolute;
  left: 0;
  top: 0;
  width: min(280px, calc(100% - 24px));
  pointer-events: auto;
  /* Dragged by hand (callout.js), not scrolled. */
  touch-action: none;
  cursor: grab;
}
.callout-box.dragging {
  cursor: grabbing;
}
/* The box: grows out of the line's end when the callout opens
   (callout.js sets --origin to that corner). */
.slab {
  position: relative;
  transform-origin: var(--origin, left bottom);
  transform: scale(0.04);
  opacity: 0;
  transition:
    transform 0.3s cubic-bezier(0.2, 0.8, 0.2, 1) 0.4s,
    opacity 0.15s 0.4s;
}
.slab-face {
  position: relative;
  margin: 0;
  padding: 12px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  background: var(--bg);
  box-shadow: inset 0 0 0 1px var(--fg);
}
.callout.shown:not(.folded) .callout-line line {
  stroke-dashoffset: 0;
}
.callout.shown:not(.folded) .slab {
  transform: none;
  opacity: 1;
}
/* Folding runs the opening backwards: the box shrinks, then the line. */
.callout.folded .slab {
  pointer-events: none;
  transition-delay: 0s;
}
.callout.folded .callout-line line {
  transition-delay: 0.2s;
}

.slab-close {
  position: absolute;
  top: 0;
  right: 0;
  width: 36px;
  height: 36px;
  padding: 0;
  font-size: 18px;
  line-height: 1;
  color: var(--mute);
}


.detail {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}
.detail b {
  font-size: 13px;
  letter-spacing: 0.04em;
  overflow-wrap: anywhere;
}
.detail span:not(.label) {
  font-size: 12px;
  letter-spacing: 0.04em;
}
.detail-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}
.slab-actions {
  display: flex;
  justify-content: flex-end;
  gap: 6px;
}
.slab-btn {
  height: 30px;
  padding: 0 10px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  box-shadow: inset 0 0 0 1px var(--rule);
}
.slab-btn.primary {
  background: var(--accent);
  color: #121212;
  box-shadow: none;
}
/* ---- Sheet (sheet.js) ------------------------------------------------------- */

/* As Tickmark's: a panel near the top, clear of the keyboard, over the
   screen blurred and dimmed. The blur itself eases in and out (sheet.js
   toggles .open); fading the element instead makes iOS show the full blur
   on the first frame. */
.sheet {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  justify-content: center;
  align-items: flex-start;
  padding: calc(env(safe-area-inset-top) + 72px) 12px 0;
  color: var(--lit);
  font: 12px/1.35 var(--mono);
  background: rgb(10 10 9 / 0);
  -webkit-backdrop-filter: blur(0px) saturate(100%);
  backdrop-filter: blur(0px) saturate(100%);
  transition:
    background-color 320ms cubic-bezier(0.4, 0, 0.2, 1),
    -webkit-backdrop-filter 320ms cubic-bezier(0.4, 0, 0.2, 1),
    backdrop-filter 320ms cubic-bezier(0.4, 0, 0.2, 1);
}
.sheet.open {
  background: rgb(10 10 9 / 0.55);
  -webkit-backdrop-filter: blur(14px) saturate(110%);
  backdrop-filter: blur(14px) saturate(110%);
}
.sheet-panel {
  width: 100%;
  max-width: 480px;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 12px;
  background: #151513;
  box-shadow: inset 0 0 0 1px var(--hair);
}
.sheet-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.sheet-cancel {
  height: 32px;
  margin-left: -8px;
  padding: 0 8px;
  color: var(--mute);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
}
.sheet-action {
  height: 32px;
  padding: 0 14px;
  background: var(--accent);
  color: #121212;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
}
.sheet-action:disabled {
  opacity: 0.5;
}
.sheet-title {
  font-family: var(--cond);
  font-size: 24px;
  font-weight: 700;
  line-height: 1.1;
}
.sheet-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.sheet-input {
  width: 100%;
  height: 48px;
  margin: 0;
  padding: 0 12px;
  border: 1px solid var(--lit);
  border-radius: 0;
  background: #1a1a18;
  color: var(--lit);
  font: 16px var(--mono); /* iOS zooms into smaller inputs */
  outline: none;
  -webkit-appearance: none;
  appearance: none;
}
.sheet-input[type='text'] {
  text-transform: uppercase;
}
/* Dates line up with the text fields: iOS otherwise draws the value
   centred and stuck to the top edge. */
.sheet-input[type='date'] {
  display: block;
  min-height: 48px;
  line-height: 46px;
  text-align: left;
  color-scheme: dark;
}
.sheet-input[type='date']::-webkit-date-and-time-value {
  margin: 0;
  text-align: left;
  line-height: 46px;
}
/* Set apart at the bottom of the edit sheet, in the accent as a warning. */
.sheet-delete {
  align-self: flex-start;
  height: 36px;
  margin-top: 8px;
  padding: 0 12px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent);
}
.sheet-note {
  margin: 0;
  line-height: 1.5;
}

.sheet-error {
  margin: 0;
  font-size: 10px;
  letter-spacing: 0.06em;
  color: var(--accent);
}
.sheet-error:empty {
  display: none;
}

/* The turntable frame that flies between the shelf and the viewer. */
.flying {
  position: fixed;
  z-index: 5;
  margin: 0;
  pointer-events: none;
  transform-origin: 0 0;
}

/* ---- Add gear ----------------------------------------------------------------- */

.add {
  /* A form-width column, centred on wide screens. */
  padding: 16px max(12px, calc((100% - 560px) / 2));
}

.field-label {
  display: block;
  margin: 0 0 8px;
  font-size: 10px;
  letter-spacing: 0.06em;
  color: var(--mute);
}

.field {
  width: 100%;
  height: 48px;
  margin: 0 0 24px;
  padding: 0 12px;
  border: 1px solid var(--lit);
  border-radius: 0;
  background: #1a1a18;
  color: var(--lit);
  font: 16px var(--mono); /* iOS zooms into smaller inputs */
  text-transform: uppercase;
  outline: none;
}
.field::placeholder {
  color: #5c5b55;
}

.steps {
  margin: 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--hair);
}
.steps li {
  display: flex;
  gap: 12px;
  padding: 12px 0;
  border-bottom: 1px solid var(--hair);
  font-size: 11px;
  letter-spacing: 0.04em;
}
.steps b {
  flex: none;
  color: var(--accent);
}

/* ---- Read-only edition (edition.js) ------------------------------------------ */

/* The public copy: no adding, editing or deleting, and no scans to watch. */
.read-only #add-button,
.read-only #scan-status,
.read-only .slab-actions {
  display: none !important;
}
