/* Wildra console.
 *
 * The tokens are the app's own, from WildraTheme.swift, not an approximation:
 * the same day palette, the same spacing scale, the same rounded face, the same
 * 16pt card. A member moving from the phone to a keyboard should not feel they
 * have changed products.
 *
 * Three of the app's rules carry over exactly:
 *
 *   The type is green-black, and it is green-black in every light. #22322A is
 *   the logo's green taken most of the way to black — it reads as black, passes
 *   AA everywhere, and warms the page without anything looking coloured.
 *
 *   inkSoft is the GREENER of the two, not the greyer. Hue is carried by mass,
 *   so small type has to be more saturated to look like the same family.
 *
 *   The mark does not change colour with the hour. The sun is always #CE6B27.
 *
 * What does NOT carry over: the five light bands. The app changes with sunrise
 * and sunset because it is used outdoors across a day. A console is used at a
 * desk, and a page that turned brown at dusk would be a novelty on a large
 * screen rather than a kindness. It follows the system's light and dark
 * setting instead — which is what the app itself does for its system chrome,
 * for the same reason.
 */

:root {
  /* Day. */
  --background: #F1EEE6;
  --surface:    #FDFBF6;
  --accent:     #CE6B27;
  --ink:        #22322A;
  --ink-soft:   #4A6558;
  --trail:      #2F5E4A;

  /* Fixed, whatever the scheme. */
  --brand-orange: #CE6B27;
  --brand-green:  #2F483D;

  /* RULE: every colour on this page is one of the six above, the brand pair,
     or a stated opacity of one of them. The app's own control ground is
     `inkSoft.opacity(0.10)` — that is where these come from, not from a guess
     at what looked close. inkSoft is #4A6558 by day, #8E9E96 by night, so the
     same rule gives a warm grey-green on cream and a cool one in the dark
     without either being written down as a colour of its own. */
  --control:   rgba(74, 101, 88, 0.10);   /* inkSoft 10% — search, chips, wells */
  --hairline:  rgba(74, 101, 88, 0.15);   /* inkSoft 15% — the app's Divider opacity */
  --raised:    rgba(74, 101, 88, 0.06);   /* inkSoft 6%  — a row under the pointer */
  --placeholder: rgba(74, 101, 88, 0.70); /* inkSoft 70% — the app draws its own */
  --empty:     rgba(74, 101, 88, 0.45);   /* inkSoft 45% — a symbol with no photo */

  /* One scale, used everywhere. "Lots of air" only works if the air is
     consistent. */
  --xs: 4px;
  --s:  8px;
  --m:  16px;
  --l:  24px;
  --xl: 40px;
  --xxl: 64px;

  --card-radius: 16px;
  --screen-margin: 20px;

  /* ui-rounded is SF Rounded, the face every heading in the app wears. */
  --rounded: ui-rounded, "SF Pro Rounded", system-ui, -apple-system, sans-serif;
  --body:    system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono:    ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
}

@media (prefers-color-scheme: dark) {
  :root {
    /* Night, from the app. In the dark the GROUND moves, not the card. */
    --background: #0C1310;
    --surface:    #18211D;
    --accent:     #CE6B27;
    --ink:        #E8EFEA;
    --ink-soft:   #8E9E96;
    --trail:      #628F7C;

    --control:     rgba(142, 158, 150, 0.12);
    --hairline:    rgba(142, 158, 150, 0.18);
    --raised:      rgba(142, 158, 150, 0.07);
    --placeholder: rgba(142, 158, 150, 0.70);
    --empty:       rgba(142, 158, 150, 0.45);
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--background);
  color: var(--ink);
  font: 16px/1.55 var(--body);
  -webkit-font-smoothing: antialiased;
}

/* ---- the lockup --------------------------------------------------------
 *
 * The same arrangement the site's masthead uses — mark, then the word with the
 * orange rule beneath it — assembled from the app's own template assets rather
 * than a second copy of the artwork. They are white with alpha, so they are
 * tinted through a mask: the line and the word take the ink, the sun and the
 * rule keep the fixed brand orange whatever else the page is doing.
 *
 * RULE, from the app: the mark does not change colour with the hour. Buttons
 * and links follow the scheme; the mark does not.
 */

.lockup {
  display: flex;
  align-items: center;
  gap: 10px;
}

.mark {
  position: relative;
  width: 42px;
  aspect-ratio: 1024 / 455;
  flex: 0 0 auto;
}

.mark span {
  position: absolute;
  inset: 0;
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
}

.mark .line {
  -webkit-mask-image: url("img/wildra-mark-line.png");
  mask-image: url("img/wildra-mark-line.png");
  background: var(--ink);
}

.mark .sun {
  -webkit-mask-image: url("img/wildra-mark-sun.png");
  mask-image: url("img/wildra-mark-sun.png");
  background: var(--brand-orange);
}

.word {
  display: flex;
  flex-direction: column;
  gap: 3px;
}

/* RULE: size from the artwork's own ratios. In the logo the word is 2048 wide
   against a 1557 mark and the rule is 1092 — so against a 42px mark they are
   55 and 29. Guessing the word narrower than the mark, when the artwork has it
   wider, is the sort of thing nobody can name and everybody notices. */
.wordmark, .rule {
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: left center;
  mask-position: left center;
}

.wordmark {
  width: 76px;
  aspect-ratio: 2048 / 275;
  -webkit-mask-image: url("img/wildra-wordmark.png");
  mask-image: url("img/wildra-wordmark.png");
  background: var(--ink);
}

.rule {
  width: 40px;
  aspect-ratio: 1092 / 46;
  -webkit-mask-image: url("img/wildra-rule.png");
  mask-image: url("img/wildra-rule.png");
  background: var(--brand-orange);
}

/* ---- chrome ---- */

header {
  display: flex;
  align-items: center;
  gap: var(--m);
  flex-wrap: wrap;
  padding: 12px var(--l);
  position: sticky;
  top: 0;
  z-index: 10;
  background: var(--surface);
  border-bottom: 1px solid var(--hairline);
}

header .brand {
  display: flex;
  align-items: center;
  gap: var(--s);
}

header .where {
  font: 12px var(--mono);
  color: var(--ink-soft);
}

header .spacer { flex: 1; }

nav { display: flex; gap: var(--xs); }

/* ---- the progress line -------------------------------------------------
 *
 * A line rather than a bar: three pixels of brand orange sitting under the
 * header, filling with green as a read completes. Green because in this app
 * green is the land and the favourable answer — the colour arriving is the
 * work being done.
 *
 * Thin deliberately. The accent marks where you are, sparingly; a full-height
 * orange block across the top of the screen would be the largest piece of
 * saturated colour on the page and would be there every time anything loads.
 *
 * RULE: THE LINE ONLY CLAIMS TO KNOW WHAT IT KNOWS. Reading a zone pages by
 * change token, and CloudKit doesn't say how many pages are coming — so that
 * read gets a travelling segment rather than a fraction. Reading every walk or
 * sweeping every zone has a real denominator, and those fill for real. A bar
 * that invents a percentage and then jumps is worse than no bar.
 */

.progress {
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 3px;
  background: var(--brand-orange);
  overflow: hidden;
}

.progress span {
  display: block;
  height: 100%;
  background: var(--trail);
  width: 0;
  transition: width 220ms ease;
}

/* No denominator: a segment travels the line instead of pretending to fill. */
.progress.is-unknown span {
  width: 30%;
  transition: none;
  animation: travel 1.1s ease-in-out infinite;
}

@keyframes travel {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(333%); }
}

/* Somebody who has asked for less movement gets a still line rather than a
   shuttling one; the status text says what is happening either way. */
@media (prefers-reduced-motion: reduce) {
  .progress.is-unknown span { animation: none; width: 100%; opacity: 0.6; }
}

/* ---- type --------------------------------------------------------------
 *
 * Heading and body sit close in size — the hierarchy is carried by WEIGHT,
 * which holds however large somebody has set their type. Only the display and
 * title sizes wear the rounded face, as in the app.
 */

h1, h2 {
  font-family: var(--rounded);
  font-weight: 600;
  letter-spacing: -0.01em;
  margin: 0;
}

h1 { font-size: 22px; }
h2 { font-size: 28px; }

.eyebrow {
  font-family: var(--rounded);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin: 0 0 var(--s);
}

.summary {
  font: 14px var(--mono);
  color: var(--ink);
  margin: 0 0 var(--s);
}

.note {
  color: var(--ink-soft);
  font-size: 14px;
  margin: 0 0 var(--m);
  max-width: 46rem;
}

.status {
  font: 13px var(--mono);
  color: var(--ink-soft);
  margin: 0 0 var(--l);
  min-height: 1.4em;
}

.status.bad { color: var(--accent); }

/* ---- buttons ---- */

button {
  font: 15px var(--body);
  font-weight: 500;
  padding: var(--s) var(--m);
  border: 1px solid transparent;
  border-radius: 999px;
  background: var(--trail);
  color: var(--surface);
  cursor: pointer;
  transition: background 120ms ease;
}

button:hover:not(:disabled) { filter: brightness(1.1); }
button:disabled { opacity: 0.35; cursor: not-allowed; }

button.quiet {
  background: var(--control);
  color: var(--trail);
  border-color: transparent;
  border-radius: 999px;
}
button.quiet:hover:not(:disabled) { background: var(--hairline); filter: none; }

button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* RULE, borrowed from the app: the accent marks where you are, and it marks it
   in TYPE. A filled orange tab would be the largest block of saturated colour
   on the page, sitting permanently at the top of the screen opened most. */
button.tab {
  background: transparent;
  color: var(--ink-soft);
  border-color: transparent;
  font-weight: 500;
}
button.tab:hover { color: var(--ink); filter: none; }
button.tab.is-on { color: var(--accent); }

/* ---- layout ---- */

main { padding: var(--l) var(--l) var(--xxl); max-width: 100rem; }

#view-locker > h2, #view-trips > h2 { margin: 0 0 2px; }
.summary { margin: 0 0 var(--m); }

section + section { margin-top: var(--xl); }

/* ---- the card ----------------------------------------------------------
 *
 * The one container the app is built from, and the reason its screens read as
 * calm: surface on background, generous radius, no border and no shadow. A
 * page made of hairlines is a spreadsheet; a page made of cards is a place.
 */

.card {
  background: var(--surface);
  border-radius: var(--card-radius);
  padding: var(--m);
  margin-bottom: var(--m);
}

.controls {
  display: flex;
  align-items: center;
  gap: var(--m);
  flex-wrap: wrap;
  background: var(--surface);
  border-radius: 12px;
  padding: 10px var(--m);
  margin-bottom: var(--m);
}

.controls label {
  font-family: var(--rounded);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

/* ---- search and filter, as the app draws them ------------------------
 *
 * A capsule on `inkSoft.opacity(0.10)` with a magnifier at the front, the
 * placeholder in inkSoft at 70%, and a clear button that appears only once
 * there is something to clear. Taken from WildraSearchBar rather than
 * approximated: the app draws its own placeholder because a browser's — like
 * a TextField's — comes in a system grey no palette can reach, and that grey
 * would be the last piece of stock colour left on the screen.
 */

.search {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 1 1 16rem;
  max-width: 26rem;
  padding: 7px 12px;
  border-radius: 999px;
  background: var(--control);
}

.search svg {
  width: 14px;
  height: 14px;
  flex: 0 0 14px;
  fill: none;
  stroke: var(--ink-soft);
  stroke-width: 1.6;
  stroke-linecap: round;
}

.search input {
  flex: 1;
  min-width: 0;
  border: 0;
  background: none;
  font: 15px var(--body);
  color: var(--ink);
  padding: 0;
}

.search input:focus { outline: none; }
.search input::placeholder { color: var(--placeholder); }

.search .clear {
  padding: 0;
  border: 0;
  background: none;
  line-height: 0;
  cursor: pointer;
}
.search .clear svg { width: 15px; height: 15px; stroke: var(--surface); }
.search .clear svg .solid { fill: var(--ink-soft); stroke: none; }

/* A pill for choosing, and a pill for a thing that is on or off. The app uses
   a menu for both; a select is the browser's menu, so it wears the same skin
   rather than pretending to be something it isn't. */
.pill, .chip {
  font: 14px var(--body);
  padding: 7px 14px;
  border: 0;
  border-radius: 999px;
  background: var(--control);
  color: var(--ink);
  cursor: pointer;
}

.pill:hover, .chip:hover { background: var(--hairline); filter: none; }

/* RULE, from the app: the accent marks what is on, and it marks it in TYPE.
   A filled orange pill would be the largest saturated block on the screen. */
.chip[aria-pressed="true"] { color: var(--accent); font-weight: 600; }

/* A filter that is doing something says so in the accent, and offers the way
   back beside it — the app puts an ✕ next to its menu for the same reason. */
#filter { max-width: 18rem; }
.controls:has(#filter:not([value="all"])) #filter { color: var(--accent); }

.clear-filter {
  padding: 0;
  border: 0;
  background: none;
  line-height: 0;
  cursor: pointer;
  margin-left: calc(var(--m) * -1 + var(--xs));
}
.clear-filter svg { width: 16px; height: 16px; stroke: var(--surface); stroke-width: 1.6; stroke-linecap: round; fill: none; }
.clear-filter svg .solid { fill: var(--ink-soft); stroke: none; }

header select {
  font: 12px var(--mono);
  padding: 5px var(--s);
  border: 0;
  border-radius: 999px;
  background: var(--control);
  color: var(--ink-soft);
}

#zone {
  min-width: 28rem;
  font: 13px var(--mono);
  border: 0;
  border-radius: 999px;
  background: var(--control);
  color: var(--ink);
  padding: 7px 12px;
}

/* ---- tables ---- */

table {
  border-collapse: collapse;
  width: 100%;
  font-size: 15px;
}

th {
  text-align: left;
  font-family: var(--rounded);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-soft);
  padding: 0 var(--m) var(--s) 0;
  border-bottom: 1px solid var(--hairline);
  white-space: nowrap;
}

td {
  padding: 10px var(--m) 10px 0;
  border-bottom: 1px solid var(--hairline);
  vertical-align: middle;
}

tbody tr:last-child td { border-bottom: none; }

th.right, td.right { text-align: right; padding-right: var(--l); }
td.dim { color: var(--ink-soft); }
td.bad { color: var(--accent); font-weight: 600; }

tbody tr.openable { cursor: pointer; }
tbody tr.openable:hover { background: var(--raised); }

/* Gear is a list of cards; the rest stay tabular. */
table.pack, table.trips, table.breakdown {
  background: var(--surface);
  border-radius: var(--card-radius);
  padding: 0;
  margin-bottom: var(--l);
}

table.pack th:first-child, table.pack td:first-child,
table.trips th:first-child, table.trips td:first-child,
table.breakdown th:first-child, table.breakdown td:first-child { padding-left: var(--m); }

table.pack th:last-child, table.pack td:last-child,
table.trips th:last-child, table.trips td:last-child,
table.breakdown th:last-child, table.breakdown td:last-child { padding-right: var(--m); }

table.pack td.right, table.trips td.right { font: 15px var(--body); font-variant-numeric: tabular-nums; }
table.breakdown { max-width: 46rem; }
table.breakdown td:first-child { font: 14px var(--mono); }

/* ---- the gear list -----------------------------------------------------
 *
 * The app's Locker, on a wider screen. One card per item: thumbnail, name, a
 * secondary line, weight on the right. A table would fit more and would stop
 * looking like Wildra.
 */

/* ---- working layout ---------------------------------------------------
 *
 * A phone stacks because it has one column to spend. A desktop has several,
 * and stacking them is how a screen ends up mostly empty with everything below
 * the fold. The list keeps the width it needs and the editor takes a column of
 * its own beside it, so nothing moves when somebody opens an item.
 */

.work {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--l);
  align-items: start;
}

.work.is-editing { grid-template-columns: minmax(0, 1fr) 24rem; }

@media (max-width: 60rem) {
  .work.is-editing { grid-template-columns: 1fr; }
}

.work-side {
  position: sticky;
  top: 84px;
}

/* Several cards across rather than one long column. 22rem is about where a
   gear name, its brand and a weight stop being cramped. */
.gear-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(22rem, 1fr));
  gap: var(--s);
  margin-bottom: var(--l);
}

.gear-row {
  display: grid;
  grid-template-columns: 56px 1fr auto;
  align-items: center;
  gap: var(--m);
  background: var(--surface);
  border-radius: var(--card-radius);
  padding: 12px var(--m);
  cursor: pointer;
  transition: background 120ms ease;
}

.gear-row:hover { background: var(--control); }
.gear-row:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.gear-row.is-archived { opacity: 0.55; }

.gear-thumb {
  width: 56px;
  height: 56px;
  border-radius: 10px;
  background: var(--control);
  overflow: hidden;
}
.gear-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

.gear-words { min-width: 0; }

.gear-name {
  margin: 0;
  font-size: 16px;
  font-weight: 600;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.gear-sub {
  margin: 2px 0 0;
  font-size: 14px;
  color: var(--ink-soft);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.gear-weight {
  margin: 0;
  font-size: 16px;
  color: var(--ink);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* ---- group headings ---- */

p.group {
  grid-column: 1 / -1;
  font-family: var(--rounded);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
  margin: var(--l) 0 var(--xs);
}

.tag {
  font-family: var(--rounded);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-soft);
  border: 1px solid var(--hairline);
  border-radius: 5px;
  padding: 2px 6px;
  margin-left: var(--s);
  vertical-align: middle;
}

/* ---- sign in and empty ---- */

.signin, .empty {
  max-width: 34rem;
  padding: var(--xl) 0;
}

.signin p, .empty p { color: var(--ink-soft); margin: 0 0 var(--m); }
.empty h2 { margin: 0 0 var(--m); }
.signin button, .empty button { margin-top: var(--s); }

/* ---- one walk ---- */

.walk-header { margin-bottom: var(--xl); }
.walk-header h2 { margin: 0 0 var(--xs); }
.walk-facts { margin: 0; font: 15px var(--body); color: var(--ink-soft); }

p.party {
  font-size: 15px;
  margin: 0 0 var(--xl);
  max-width: 46rem;
}

.photo-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
  gap: var(--s);
  margin-bottom: var(--xl);
}
.photo-grid figure {
  margin: 0;
  position: relative;
  aspect-ratio: 1;
  background: var(--control);
  border-radius: 10px;
  overflow: hidden;
}
.photo-grid img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* RULE: the two exceptions, and they are the same exception. Type sitting ON a
   photograph can't take a palette colour, because the photograph is whatever it
   is — so a heart and a duration are white with a shadow, which is what the app
   does over a picture too. Nothing else on this page is outside the palette. */
.photo-grid .heart {
  position: absolute;
  top: var(--s);
  right: 10px;
  color: var(--brand-orange);
  font-size: 15px;
  text-shadow: 0 1px 3px rgba(0,0,0,0.45);
}
.photo-grid .film {
  position: absolute;
  bottom: var(--s);
  left: 10px;
  font: 11px var(--mono);
  color: #fff;
  text-shadow: 0 1px 3px rgba(0,0,0,0.6);
}

.note-card {
  background: var(--surface);
  border-radius: var(--card-radius);
  padding: var(--m);
  margin: 0 0 var(--s);
  max-width: 46rem;
}
.note-card .decision {
  font-family: var(--rounded);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
  margin: 0 0 var(--xs);
}
.note-card .note-body { margin: 0; white-space: pre-wrap; }
.note-card .note-when { margin: var(--xs) 0 0; font: 13px var(--body); color: var(--ink-soft); }

/* ---- editing ---- */

.editor {
  background: var(--surface);
  border-radius: var(--card-radius);
  padding: var(--m);
  max-height: calc(100vh - 108px);
  overflow-y: auto;
}

/* Two to a row where they are short: a weight and a price beside each other
   read as a pair, and stacking every field is what made the form a column of
   boxes taller than the screen. */
.editor .fields {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 var(--s);
}
.editor .field.wide { grid-column: 1 / -1; }

.editor .field { display: block; margin-bottom: var(--s); }
.editor .field-label {
  display: block;
  font-family: var(--rounded);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: var(--xs);
}
.editor .field input {
  width: 100%;
  font: 16px var(--body);
  padding: 9px 12px;
  border: 1px solid transparent;
  border-radius: 10px;
  background: var(--control);
  color: var(--ink);
}
.editor .field input:focus {
  outline: none;
  border-color: var(--accent);
}
.editor .note { margin: var(--m) 0; font-size: 13px; }
.editor .status { margin: 0 0 var(--m); }
.editor-buttons { display: flex; gap: var(--s); }

.photo-field {
  display: flex;
  align-items: center;
  gap: var(--m);
  margin-bottom: var(--m);
  padding-bottom: var(--m);
  border-bottom: 1px solid var(--hairline);
}

.photo-thumb {
  width: 72px;
  height: 72px;
  flex: 0 0 72px;
  background: var(--control);
  border-radius: 10px;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}
.photo-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.photo-none { font: 11px var(--mono); color: var(--empty); }
.photo-controls { flex: 1; min-width: 0; }
.photo-buttons { display: flex; gap: var(--xs); margin-bottom: var(--xs); }
.photo-controls .note { margin: 0; font-size: 12px; }

button.small { padding: 5px 10px; font-size: 13px; }

/* The whole row is a drop target, so it says so while something is over it. */
.photo-field {
  border-radius: 12px;
  transition: background 120ms ease;
}
.photo-field.is-dropping {
  background: var(--control);
  outline: 2px dashed var(--accent);
  outline-offset: -4px;
}

/* ---- diagnostics ---- */

#sweep-section { margin-bottom: var(--xl); }
#sweep-examples, details pre {
  font: 12px/1.7 var(--mono);
  background: var(--surface);
  border-radius: var(--card-radius);
  padding: var(--m);
  margin-top: var(--m);
  max-width: 46rem;
  overflow: auto;
}

details { margin-top: var(--xl); }
details summary {
  cursor: pointer;
  font: 13px var(--mono);
  color: var(--ink-soft);
}

#trips-progress { font: 13px var(--body); }
tr.bad-row td:first-child { color: var(--accent); }

/* ---- footer ---- */

footer {
  padding: var(--l) var(--screen-margin) var(--xl);
  border-top: 1px solid var(--hairline);
}
footer p {
  margin: 0;
  font-size: 14px;
  color: var(--ink-soft);
  max-width: 46rem;
}

/* ---- import ---- */

.work.is-importing { grid-template-columns: minmax(0, 1fr) 30rem; }

@media (max-width: 72rem) {
  .work.is-importing { grid-template-columns: 1fr; }
}

.importer {
  background: var(--surface);
  border-radius: var(--card-radius);
  padding: var(--m);
  max-height: calc(100vh - 108px);
  overflow-y: auto;
}

.drop {
  border-radius: 12px;
  background: var(--control);
  padding: var(--m);
  margin-bottom: var(--m);
  transition: outline-color 120ms ease;
  outline: 2px dashed transparent;
  outline-offset: -4px;
}

.drop.is-dropping { outline-color: var(--accent); }
.drop p { margin: 0 0 var(--s); font-size: 14px; color: var(--ink-soft); }

.paste {
  width: 100%;
  margin-top: var(--s);
  font: 13px var(--mono);
  padding: var(--s);
  border: 0;
  border-radius: 8px;
  background: var(--surface);
  color: var(--ink);
  resize: vertical;
}
.paste:focus { outline: 2px solid var(--accent); outline-offset: -2px; }
.paste::placeholder { color: var(--placeholder); }

table.mapping { font-size: 14px; margin-bottom: var(--m); }
table.mapping td { padding: 4px var(--s) 4px 0; border: 0; }
table.mapping td:first-child { width: 40%; font-weight: 500; }
table.mapping select { max-width: 100%; }

.import-preview { margin-top: var(--m); }
.note.warn { color: var(--accent); }

.lighterpack {
  display: flex;
  gap: var(--xs);
  margin-top: var(--s);
  padding-top: var(--s);
  border-top: 1px solid var(--hairline);
}
.lighterpack input {
  flex: 1;
  min-width: 0;
  font: 13px var(--mono);
  padding: 6px 10px;
  border: 0;
  border-radius: 999px;
  background: var(--surface);
  color: var(--ink);
}
.lighterpack input:focus { outline: 2px solid var(--accent); outline-offset: -2px; }
.lighterpack input::placeholder { color: var(--placeholder); }

/* ---- importer, quietened ---- */

.drop-line { margin: 0 0 var(--s) !important; }

.mapper { margin-top: var(--l); }
.mapper summary {
  cursor: pointer;
  font-size: 13px;
  color: var(--ink-soft);
  list-style: none;
}
.mapper summary::-webkit-details-marker { display: none; }
.mapper summary::before { content: "▸ "; }
.mapper[open] summary::before { content: "▾ "; }
.mapper table { margin-top: var(--s); }

.import-preview .summary { font: 15px var(--body); margin: 0 0 var(--s); }
.import-preview .field { margin-top: var(--s); }

/* ---- archive and delete ---- */

.editor-shelf {
  display: flex;
  gap: var(--s);
  align-items: center;
  margin-top: var(--l);
  padding-top: var(--m);
  border-top: 1px solid var(--hairline);
  flex-wrap: wrap;
}

/* The bin is not a peer of the other buttons: no fill, accent type only. A
   filled red button sitting under every gear item would make deleting look
   like one of the things you are meant to do. */
button.danger {
  background: none;
  border: 0;
  color: var(--accent);
  padding: 5px 10px;
}
button.danger:hover:not(:disabled) { background: var(--control); filter: none; }

.editor-shelf .note { margin: 0 0 var(--s); flex: 1 1 100%; }

/* ---- choosing several ---- */

.picked {
  display: flex;
  align-items: center;
  gap: var(--s);
  flex-wrap: wrap;
  background: var(--surface);
  border-radius: 12px;
  padding: 10px var(--m);
  margin-bottom: var(--m);
  position: sticky;
  top: 76px;
  z-index: 5;
}

.picked-count {
  margin: 0;
  font-size: 14px;
  color: var(--ink-soft);
  flex: 1 1 14rem;
}

.picked .note { margin: 0 0 var(--s); flex: 1 1 100%; }

/* A card being chosen: the tick is the accent, the card is not. Filling sixty
   cards with orange would make a Locker look like an alarm. */
.gear-row.is-picking { cursor: pointer; }

.gear-row .tick {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: 1.5px solid var(--hairline);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  color: var(--surface);
  flex: 0 0 20px;
}

.gear-row.is-picking { grid-template-columns: 20px 56px 1fr auto; }

.gear-row.is-chosen { background: var(--control); }
.gear-row.is-chosen .tick {
  background: var(--accent);
  border-color: var(--accent);
}

/* The walk editor sits above the walk it edits — the walk is the page, not a
   column beside it, so there is nothing to put the editor next to. */
#walk-editor { max-width: 34rem; margin-bottom: var(--l); }
#walk-editor[hidden] { display: none; }
#walk-editor .editor { padding: var(--m); }
#walk-editor .fields { grid-template-columns: 1fr 1fr; }

/* A walk with no Trip record is a real fault, not a style — the zone exists
   and the thing it is supposed to hold does not. */
.gear-row.is-broken .gear-name { color: var(--accent); }

#trips-picked { top: 76px; }


/* ---- less like a terminal ---------------------------------------------
 *
 * Mono had spread to things that are not machine output: how many walks
 * somebody has done, how heavy their pack is, when they bought a jacket. The
 * app sets those in its own face, and a fact about somebody's walking read in
 * a terminal typeface is most of what made this look unfinished.
 *
 * Mono stays where it is honest — zone names, record identifiers, the
 * container — all of which now live in Diagnostics.
 */

.summary {
  font: 15px var(--body);
  color: var(--ink-soft);
}

.walk-facts { font: 15px var(--body); }

.gear-row .gear-weight { font-variant-numeric: tabular-nums; }

/* The two screens now open the same way, so they should look the same doing
   it. */
#view-locker > h2, #view-trips > h2 { margin: 0 0 2px; }
#view-locker > .summary, #view-trips > .summary { margin: 0 0 var(--m); }

/* Sign out is the least likely thing anybody wants, so it stops looking like
   an invitation. */
#session .tab { color: var(--ink-soft); }
#session .tab:hover { color: var(--ink); }

/* The diagnostics controls carry the container name, which IS machine
   output and stays in mono. */
#view-diagnostics .where { font: 12px var(--mono); color: var(--ink-soft); }

/* Three across at most. Four left each card mostly empty with the weight
   marooned at the far end. */
.gear-list { grid-template-columns: repeat(auto-fill, minmax(26rem, 1fr)); }

@media (min-width: 110rem) {
  .gear-list { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}


/* ---- hidden means hidden -----------------------------------------------
 *
 * The `hidden` attribute sets `display: none` at the very bottom of the
 * cascade, so ANY class that sets a display wins over it — and every element
 * here that is shown and hidden by that attribute also sets one. The result is
 * a bar that never goes away, which is exactly what happened to the selection
 * row twice.
 */

.picked[hidden],
.controls[hidden],
.drop[hidden],
.lighterpack[hidden],
.editor-shelf[hidden],
.work-side[hidden],
.gear-list[hidden] { display: none; }

/* ---- the lockup, unclipped --------------------------------------------
 *
 * The word and the rule are masks on fixed-ratio boxes, and a flex parent was
 * free to shrink them — which crops a mask rather than scaling it, so the
 * final letter simply vanished.
 */

.lockup { flex: 0 0 auto; }
.word { flex: 0 0 auto; }
.wordmark, .rule { flex: 0 0 auto; }

/* Sign out is a button again — quiet, but a button. It is a different KIND of
   thing from the tabs beside it, and dressing it as one of them made it read
   as a place to go rather than an action to take. */
#session .tab {
  background: var(--control);
  border-radius: 999px;
  padding: 6px 14px;
  color: var(--ink-soft);
}
#session .tab:hover { background: var(--hairline); color: var(--ink); }

/* Archived gear sits in its own group at the end. The heading is the softer
   ink rather than the accent — it is a shelf, not somewhere to be sent. */
p.group.is-archived { color: var(--ink-soft); }




/* ---- the progress line, with more presence -----------------------------
 *
 * Dan's call, and the right one: the orange-and-green line is the signal, not
 * a system cursor. A spinning wheel is the operating system saying the
 * application has stopped responding — the opposite of what a working page
 * should tell somebody.
 *
 * So the line does the whole job, and does it more visibly: four pixels, a
 * soft glow at the leading edge so the eye finds it, and a travelling band
 * when there is no total to count towards.
 */

.progress {
  height: 4px;
  bottom: -2px;
  background: var(--brand-orange);
  z-index: 20;
  transition: opacity 250ms ease;
}

.progress span {
  background: var(--trail);
  box-shadow: 0 0 10px 1px var(--trail);
  transition: width 260ms cubic-bezier(0.2, 0.8, 0.2, 1);
}

/* No denominator: a band travels the line rather than pretending to fill. The
   width and easing are tuned so it reads as movement at a glance from across a
   desk, which is where somebody actually is while a hundred records save. */
.progress.is-unknown span {
  width: 34%;
  transition: none;
  animation: travel 1.25s cubic-bezier(0.45, 0, 0.55, 1) infinite;
}

@keyframes travel {
  0%   { transform: translateX(-110%); }
  100% { transform: translateX(400%); }
}

@media (prefers-reduced-motion: reduce) {
  .progress.is-unknown span {
    animation: none;
    width: 100%;
    opacity: 0.55;
    box-shadow: none;
  }
}

/* ---- walks are bigger than gear ----------------------------------------
 *
 * A walk has more to show than a jacket does — a photograph worth looking at,
 * a place, dates, what was carried — so its card is taller and its cover
 * larger. The Locker's card is right for a list of a hundred things; it is
 * mean for a list of five.
 *
 * And the grid uses auto-FIT rather than auto-fill, which is the honest
 * version of "scale with how many there are": empty columns collapse, so five
 * walks stretch to fill the screen and forty settle into three. Nothing
 * decides a size from a count — the layout simply uses the room it has.
 */

#view-trips .gear-list {
  grid-template-columns: repeat(auto-fit, minmax(30rem, 1fr));
  gap: var(--m);
}

#view-trips .gear-row {
  grid-template-columns: 96px 1fr auto;
  gap: var(--m);
  padding: var(--m);
  align-items: center;
}

#view-trips .gear-row.is-picking {
  grid-template-columns: 20px 96px 1fr auto;
}

#view-trips .gear-thumb {
  width: 96px;
  height: 96px;
  border-radius: 12px;
}

#view-trips .gear-name {
  font-size: 19px;
  letter-spacing: -0.01em;
  white-space: normal;
}

#view-trips .gear-sub {
  margin-top: 5px;
  font-size: 15px;
  white-space: normal;
}

#view-trips .gear-weight {
  font-size: 18px;
  align-self: center;
}

/* Above a certain width three walks across is plenty — beyond that the cover
   photograph starts to look like a thumbnail again. */
@media (min-width: 120rem) {
  #view-trips .gear-list { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* ---- the lockup, pinned ------------------------------------------------
 *
 * Three masks on fixed-size boxes. A mask that gets squeezed CROPS rather than
 * scaling, so a flex parent free to shrink these takes the last letter off the
 * word. Every dimension is stated and nothing may shrink.
 */

header { overflow: visible; }

.lockup { flex: 0 0 auto; min-width: 0; overflow: visible; }

.word {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  flex: 0 0 auto;
  width: 80px;
}

.wordmark {
  width: 80px;
  height: 11px;
  aspect-ratio: auto;
  -webkit-mask-size: 80px 11px;
  mask-size: 80px 11px;
}

.rule {
  width: 42px;
  height: 2px;
  aspect-ratio: auto;
  -webkit-mask-size: 42px 2px;
  mask-size: 42px 2px;
  -webkit-mask-position: center;
  mask-position: center;
}

.mark { flex: 0 0 46px; width: 46px; height: 20px; aspect-ratio: auto; }

/* ---- what the page says --------------------------------------------------
 *
 * The status line speaks to a member — "Fetching your gear…", "Put away." —
 * so it wears the app's face, not the terminal's. Mono is for machine output,
 * which now lives entirely in Diagnostics.
 */

.status {
  font: 14px var(--body);
  color: var(--ink-soft);
}

.status.bad { color: var(--accent); }

/* ---- writing about a walk ---- */

#walk-notes-panel { max-width: 44rem; margin-bottom: var(--l); }
#walk-notes-panel[hidden] { display: none; }

.composer {
  margin-top: var(--l);
  padding-top: var(--m);
  border-top: 1px solid var(--hairline);
}

.composer .paste {
  background: var(--control);
  font: 16px/1.55 var(--body);
  padding: 12px;
  min-height: 8rem;
}

.note-card .paste {
  background: var(--control);
  font: 16px/1.55 var(--body);
  padding: 10px;
  margin-bottom: var(--s);
}

.note-card .photo-buttons { margin-top: var(--s); }

/* The Notes panel owns the notes while it is open. */
body.notes-open .walk-notes-read { display: none; }

/* ---- the pack planner --------------------------------------------------
 *
 * The Locker on one side, the pack on the other, the weight above both. This
 * is the layout a phone cannot do, and the reason the console exists.
 */

#walk-pack-panel { margin-bottom: var(--l); }
#walk-pack-panel[hidden] { display: none; }

.planner {
  background: var(--surface);
  border-radius: var(--card-radius);
  padding: var(--m);
}

.planner-head { margin-bottom: var(--m); }

/* The weight is the answer this screen exists to give, so it is the biggest
   thing on it. */
.planner-total {
  font-family: var(--rounded);
  font-size: 34px;
  font-weight: 600;
  letter-spacing: -0.02em;
  margin: 0;
  color: var(--ink);
}

.planner-detail {
  margin: 2px 0 0;
  font-size: 14px;
  color: var(--ink-soft);
}

.planner-columns {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--l);
  align-items: start;
}

@media (max-width: 68rem) {
  .planner-columns { grid-template-columns: 1fr; }
}

.planner-side { min-width: 0; }

.planner-results {
  margin-top: var(--s);
  max-height: 60vh;
  overflow-y: auto;
}

.planner-row {
  display: flex;
  align-items: center;
  gap: var(--s);
  padding: 8px 10px;
  border-radius: 10px;
}

.planner-row:hover { background: var(--control); }
.planner-row .gear-words { flex: 1; min-width: 0; }
.planner-row .gear-name { font-size: 15px; }
.planner-row .gear-sub { font-size: 13px; }

/* Already in the pack: still listed, because knowing it is there is the
   point — just not offered again. */
.planner-row.is-in { opacity: 0.5; }

/* Gear that has left the Locker since it was packed. It still weighs
   something on the day, so it is shown rather than quietly dropped. */
.planner-row.is-missing .gear-name { color: var(--accent); }

.planner-controls { display: flex; gap: 4px; flex: 0 0 auto; }
.planner-controls button.is-on { color: var(--accent); font-weight: 600; }

.planner .group { margin: var(--m) 0 var(--xs); }
.planner .search { max-width: none; }

.planner-controls-row {
  display: flex;
  gap: var(--s);
  align-items: center;
  margin-top: var(--s);
}
.planner-controls-row .search { flex: 1 1 auto; min-width: 0; }
.planner-controls-row .pill { flex: 0 0 auto; max-width: 13rem; }

.planner-thumb {
  width: 36px;
  height: 36px;
  flex: 0 0 36px;
  border-radius: 8px;
  background: var(--control);
  overflow: hidden;
}
.planner-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

.planner-side .picked {
  position: static;
  margin: var(--s) 0 0;
  padding: 8px 12px;
}
.planner-side .picked[hidden] { display: none; }
.planner-row.is-picking { cursor: pointer; }
.planner-row.is-chosen { background: var(--control); }
.planner-row .tick {
  width: 18px; height: 18px; flex: 0 0 18px;
  border-radius: 50%;
  border: 1.5px solid var(--hairline);
  display: flex; align-items: center; justify-content: center;
  font-size: 11px; color: var(--surface);
}
.planner-row.is-chosen .tick { background: var(--accent); border-color: var(--accent); }

/* ---- the quantity stepper ----
 *
 * The count sits between the two buttons, because three pairs of socks is an
 * ordinary thing to pack and the answer should be in one place.
 */

.stepper {
  display: flex;
  align-items: center;
  gap: 2px;
  background: var(--control);
  border-radius: 999px;
  padding: 2px;
}

.stepper button {
  background: none;
  padding: 3px 9px;
  min-width: 0;
  font-size: 15px;
  line-height: 1;
}
.stepper button:hover:not(:disabled) { background: var(--hairline); filter: none; }

.stepper-count {
  min-width: 1.4rem;
  text-align: center;
  font-size: 14px;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

/* A packed row is a list of things you already chose, so its picture can be
   smaller than the one you are choosing from. */
.planner-thumb.is-small {
  width: 28px;
  height: 28px;
  flex: 0 0 28px;
  border-radius: 6px;
}

/* Numbers about somebody's gear are not machine output. */
.planner-row .gear-sub { font-variant-numeric: tabular-nums; }


/* ---- mono is for machine output, and nothing else ----------------------
 *
 * It had spread to how heavy a pack is, when a note was written, how long a
 * walk ran — facts about somebody's walking, set in the typeface of a
 * terminal. Numbers still line up, because tabular figures do that without
 * borrowing the look.
 *
 * What keeps it: zone names, record identifiers, the container, the schema
 * mirror. All of which now live in Diagnostics.
 */

.planner-total,
.planner-detail,
.gear-weight,
.summary,
td.right { font-variant-numeric: tabular-nums; }

/* A picture in the pack table, matching the Locker's rows. */
table.pack td.thumb, table.pack th.thumb { width: 44px; padding-left: var(--m); padding-right: 0; }

.row-thumb {
  width: 32px;
  height: 32px;
  border-radius: 6px;
  background: var(--control);
  overflow: hidden;
}
.row-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* A vessel's amount is wider than a count, so the stepper doesn't jump. */
.stepper-count.is-volume { min-width: 3.6rem; }

/* A panel is a screen now, so it says which walk it belongs to. */
.panel-where {
  font-family: var(--rounded);
  font-size: 20px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--ink);
  margin: 0 0 var(--m);
}

.planner-head-row .panel-where { margin: 0; }

/* The composer leads, so it has no rule above it. */
.composer.is-first { margin-top: 0; padding-top: 0; border-top: 0; }

/* Room between a picture and the name it belongs to. */
table.pack td.thumb, table.pack th.thumb { padding-right: 12px; }

/* ---- the walk, as an overview ------------------------------------------
 *
 * Each part of a walk is a card that says its shape and offers a way into the
 * detail. The pack had forty rows at the top of the page and everything else
 * underneath, where nobody scrolled.
 */

.walk-section {
  background: var(--surface);
  border-radius: var(--card-radius);
  padding: var(--m);
  margin-bottom: var(--m);
  max-width: 52rem;
}

.walk-section-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--m);
  margin-bottom: var(--s);
}

.walk-figure {
  font-family: var(--rounded);
  font-size: 28px;
  font-weight: 600;
  letter-spacing: -0.02em;
  margin: 2px 0 0;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

.walk-figure-detail {
  margin: 2px 0 0;
  font-size: 14px;
  color: var(--ink-soft);
}

/* What the pack is made of, at a glance. Lengths are relative to the heaviest
   category, so the eye reads the shape rather than comparing numbers. */
.walk-bars { display: flex; flex-direction: column; gap: 6px; margin-top: var(--m); }

.walk-bar {
  display: grid;
  grid-template-columns: 8rem 1fr auto;
  align-items: center;
  gap: var(--s);
  font-size: 14px;
}

.walk-bar-name { color: var(--ink); text-transform: capitalize; }

.walk-bar-track {
  height: 6px;
  border-radius: 3px;
  background: var(--control);
  overflow: hidden;
}

.walk-bar-fill { display: block; height: 100%; background: var(--trail); }

.walk-bar-figure {
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.walk-section .note-card { background: var(--control); margin-top: var(--s); }

/* ---- leaving your plans ---- */

#walk-plans-panel { max-width: 40rem; margin-bottom: var(--l); }
#walk-plans-panel[hidden] { display: none; }

.field-hint {
  display: block;
  margin-top: 4px;
  font-size: 13px;
  color: var(--ink-soft);
}

.contact-list { display: flex; flex-direction: column; gap: 2px; margin-bottom: var(--m); }

.contact-row {
  display: flex;
  align-items: center;
  gap: var(--s);
  padding: 8px 10px;
  border-radius: 10px;
  cursor: pointer;
}
.contact-row:hover { background: var(--control); }
.contact-row input { accent-color: var(--accent); }
.contact-row .gear-name { font-size: 15px; }

#walk-plans-panel textarea.paste { background: var(--control); font: 16px/1.55 var(--body); }

/* The one sentence on this screen that could get somebody hurt if it were
   missed. It is not a note; it is the point. */
.plans-warning {
  background: var(--control);
  border-left: 3px solid var(--accent);
  border-radius: 8px;
  padding: 12px var(--m);
  margin: var(--m) 0 var(--l);
}

.plans-warning-head {
  margin: 0 0 4px;
  font-weight: 600;
  color: var(--accent);
}

.plans-warning p:last-child {
  margin: 0;
  font-size: 14px;
  color: var(--ink);
}

/* ---- the pack list, on screen ---- */

#walk-print-panel { max-width: 46rem; margin-bottom: var(--l); }
#walk-print-panel[hidden] { display: none; }

.sheet {
  background: var(--surface);
  border-radius: var(--card-radius);
  padding: var(--l);
  max-width: 46rem;
}

.sheet-head { margin-bottom: var(--m); }
.sheet h1 {
  font-family: var(--rounded);
  font-size: 26px;
  font-weight: 600;
  margin: 0;
  color: var(--ink);
}
.sheet-dates { margin: 2px 0 0; color: var(--ink-soft); font-size: 15px; }
.sheet-summary {
  margin: 0 0 var(--l);
  font-size: 15px;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}

.sheet-group { margin-bottom: var(--m); }

.sheet-group-head {
  display: flex;
  justify-content: space-between;
  font-family: var(--rounded);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-soft);
  border-bottom: 1px solid var(--hairline);
  padding-bottom: 4px;
  margin: 0 0 var(--xs);
}
.sheet-group-figure { font-variant-numeric: tabular-nums; }

.sheet-row {
  display: grid;
  grid-template-columns: 20px 1fr auto;
  align-items: baseline;
  gap: var(--s);
  padding: 5px 0;
  font-size: 15px;
}

/* The box is why this is paper. */
.sheet-box {
  width: 13px;
  height: 13px;
  border: 1.5px solid var(--ink-soft);
  border-radius: 3px;
  display: block;
  align-self: center;
}

.sheet-name { color: var(--ink); }
.sheet-extra { color: var(--ink-soft); margin-left: var(--s); font-size: 14px; }
.sheet-grams { color: var(--ink-soft); font-variant-numeric: tabular-nums; }

.sheet-foot {
  margin: var(--l) 0 0;
  padding-top: var(--s);
  border-top: 1px solid var(--hairline);
  font-size: 13px;
  color: var(--ink-soft);
}

/* ---- on paper ----------------------------------------------------------
 *
 * A different document, not the screen with its furniture hidden. No colour,
 * no card, no photographs — black on white, because that is what a printer
 * does well and what a kitchen floor needs at seven in the morning.
 */

@media print {
  header, footer, nav, .no-print, .controls, .picked { display: none !important; }

  body {
    background: #fff;
    color: #000;
    font-size: 11pt;
  }

  main { padding: 0; max-width: none; }

  .sheet {
    background: #fff;
    border-radius: 0;
    padding: 0;
    max-width: none;
  }

  .sheet h1 { font-size: 20pt; color: #000; }
  .sheet-dates, .sheet-summary, .sheet-grams, .sheet-extra, .sheet-foot { color: #333; }

  .sheet-group-head {
    color: #000;
    border-bottom: 1px solid #999;
  }

  .sheet-box { border-color: #000; }

  /* A category split across a page break is a category you tick twice. */
  .sheet-group { break-inside: avoid; }
  .sheet-row { break-inside: avoid; padding: 3px 0; }

  @page { margin: 18mm; }
}

/* The lockup on the sheet, sitting above the walk's name. */
.sheet-mark {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: var(--m);
}

@media print {
  .sheet-mark { margin-bottom: 10mm; }
  .sheet-head h1 { margin-top: 0; }
}


/* The lockup on the sheet: plain images with the colours baked in, so a
   printer and a PDF exporter both get exactly what is on screen. */
.sheet-mark { display: flex; align-items: center; gap: 10px; margin-bottom: var(--m); }
.sheet-mark-image { width: 46px; height: auto; display: block; }
.sheet-word { display: flex; flex-direction: column; align-items: center; gap: 3px; }
.sheet-word-image { width: 80px; height: auto; display: block; }
.sheet-rule-image { width: 42px; height: auto; display: block; }

@media print {
  .sheet-mark { margin-bottom: 8mm; }
}

/* ---- where a walk is up to ---------------------------------------------
 *
 * Only the state that is still ahead of somebody gets colour. "On trail" is
 * the one that matters most and the only one in the accent — a list where
 * every tag is orange says nothing.
 */

.tag.state-onTrail { color: var(--accent); border-color: var(--accent); font-weight: 600; }
.tag.state-ready { color: var(--trail); border-color: var(--trail); }
.tag.state-idea { font-style: italic; }

/* A walk that is imminent, or happening. The whole line takes the colour
   rather than a badge, because the thing worth noticing is the sentence. */
.gear-sub.is-soon { color: var(--accent); }

/* The state tags are gone from the cards; these no longer apply. */
.tag.state-onTrail, .tag.state-ready, .tag.state-idea { all: unset; }

/* The walk under way. The app gives it the whole top of the screen; here it
   takes the full width of the grid and a larger cover, which is the same idea
   with more room. */
#view-trips .gear-row.is-now {
  grid-column: 1 / -1;
  grid-template-columns: 140px 1fr auto;
  padding: var(--l);
}

#view-trips .gear-row.is-now .gear-thumb {
  width: 140px;
  height: 140px;
  border-radius: var(--card-radius);
}

#view-trips .gear-row.is-now .gear-name { font-size: 26px; }
#view-trips .gear-row.is-now .gear-sub { font-size: 16px; }
#view-trips .gear-row.is-now .gear-weight { font-size: 22px; }

/* ---- weighing ---- */

.work.is-weighing { grid-template-columns: minmax(0, 1fr) 28rem; }

@media (max-width: 70rem) {
  .work.is-weighing { grid-template-columns: 1fr; }
}

#weigher .editor { max-height: calc(100vh - 108px); overflow-y: auto; }

.weigh-list { margin: var(--m) 0; }

.weigh-row {
  display: flex;
  align-items: center;
  gap: var(--s);
  padding: 6px 0;
}

.weigh-row .gear-words { flex: 1; min-width: 0; }
.weigh-row .gear-name { font-size: 15px; }
.weigh-row .gear-sub { font-size: 13px; }

.weigh-row input {
  width: 6.5rem;
  flex: 0 0 6.5rem;
  font: 16px var(--body);
  font-variant-numeric: tabular-nums;
  text-align: right;
  padding: 7px 10px;
  border: 1px solid transparent;
  border-radius: 10px;
  background: var(--control);
  color: var(--ink);
}

.weigh-row input:focus { outline: none; border-color: var(--accent); }
.weigh-row input::placeholder { color: var(--placeholder); }

/* Saved. The row stays where it is so nothing moves under your hands, but it
   stops asking for a number. */
.weigh-row input.is-done { color: var(--ink-soft); background: transparent; }

.weigh-list .group:first-child { margin-top: 0; }



/* Where the interesting part is. A dot, because a point is what is stored —
   a rectangle would suggest a crop, which this deliberately is not. */
.focus-dot {
  position: absolute;
  width: 18px;
  height: 18px;
  margin: -9px 0 0 -9px;
  border-radius: 50%;
  border: 2px solid #fff;
  background: var(--brand-orange);
  box-shadow: 0 0 0 1px rgba(0,0,0,0.35), 0 1px 6px rgba(0,0,0,0.4);
  pointer-events: none;
}

.photo-stage .gear-sub { margin: var(--s) 0; }
.photo-stage .photo-buttons { margin-bottom: var(--s); }
.photo-stage .note { margin: 0; font-size: 13px; }

.photo-tile {
  position: relative;
  flex: 0 0 84px;
  width: 84px;
  height: 84px;
  padding: 0;
  border: 2px solid transparent;
  border-radius: 10px;
  overflow: hidden;
  background: var(--control);
  cursor: pointer;
}

.photo-tile img { width: 100%; height: 100%; object-fit: cover; display: block; }
.photo-tile.is-open { border-color: var(--accent); }
.photo-tile:hover { filter: none; opacity: 0.9; }

.photo-tile .heart {
  position: absolute;
  top: 4px;
  right: 5px;
  color: var(--brand-orange);
  font-size: 13px;
  text-shadow: 0 1px 3px rgba(0,0,0,0.5);
}

.photo-tile .cover-flag {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  background: rgba(0,0,0,0.55);
  color: #fff;
  font-size: 10px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 2px 0;
}

.photo-buttons button.is-on { color: var(--accent); }


/* ---- photographs, as a viewer ------------------------------------------
 *
 * One at a time, large, with arrows and the arrow keys. A grid is a filing
 * cabinet; this is for looking.
 */

#walk-photos-panel { max-width: 62rem; margin-bottom: var(--l); }
#walk-photos-panel[hidden] { display: none; }

.viewer-frame {
  position: relative;
  border-radius: var(--card-radius);
  overflow: hidden;
  background: var(--control);
  line-height: 0;
}

/* RULE: THE WHOLE VIEWER FITS THE SCREEN.
 *
 * The picture had 62vh of its own, and then the counter, the buttons and the
 * strip below it — so the panel ran off the bottom and the thumbnails were
 * cut in half. The frame takes what is left after the rest of the panel, not
 * a share of the window. */
.viewer-frame {
  height: min(56vh, 40rem);
}

.viewer-frame img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}

/* The small copy underneath, the full one over it once it has arrived. */
.viewer-quick { filter: blur(1px); }
.viewer-full { opacity: 0; transition: opacity 180ms ease; }
.viewer-full.is-here { opacity: 1; }

/* Over the picture rather than beside it, so the image gets the whole width —
   and dark enough to be found on a snowfield. */
.viewer-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.45);
  color: #fff;
  font-size: 26px;
  line-height: 1;
  cursor: pointer;
  opacity: 0;
  transition: opacity 140ms ease;
}

.viewer-frame:hover .viewer-arrow,
.viewer-arrow:focus-visible { opacity: 1; }
.viewer-arrow:hover { background: rgba(0, 0, 0, 0.65); filter: none; }
.viewer-arrow.is-back { left: 12px; }
.viewer-arrow.is-forward { right: 12px; }

.viewer-facts {
  margin: var(--s) 0;
  font-size: 14px;
  color: var(--ink-soft);
  font-variant-numeric: tabular-nums;
}

.viewer .photo-buttons { margin-bottom: var(--s); }

/* The rest of them, along the bottom — for jumping, not for browsing. */
.viewer-strip {
  display: flex;
  gap: 6px;
  overflow-x: auto;
  padding: var(--s) 0;
  margin-bottom: var(--s);
}

.viewer-tile {
  position: relative;
  flex: 0 0 64px;
  width: 64px;
  height: 64px;
  padding: 0;
  border: 2px solid transparent;
  border-radius: 8px;
  overflow: hidden;
  background: var(--control);
  cursor: pointer;
}

.viewer-tile img { width: 100%; height: 100%; object-fit: cover; display: block; }
.viewer-tile.is-open { border-color: var(--accent); }
.viewer-tile:hover { filter: none; opacity: 0.85; }

.viewer-tile .heart {
  position: absolute;
  top: 3px;
  right: 4px;
  color: var(--brand-orange);
  font-size: 11px;
  text-shadow: 0 1px 3px rgba(0,0,0,0.5);
}

.viewer-tile .cover-flag {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  background: rgba(0,0,0,0.55);
  color: #fff;
  font-size: 8px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 1px 0;
}

.photo-buttons button.is-on { color: var(--accent); }

/* A photograph is the full stored copy — several megabytes — so it is worth
   saying it is on its way. */
.viewer-frame.is-loading::after {
  content: "Loading…";
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font: 14px var(--body);
  color: var(--ink-soft);
}

/* ---- the membership gate ------------------------------------------------ */

/* A console that will not write says so once, at the top, and then gets out of
   the way. Not an error: nothing has gone wrong, and red would say it had. */
.gate {
  background: var(--surface);
  border-radius: var(--card-radius);
  padding: var(--m) var(--l);
  margin-bottom: var(--l);
  box-shadow: inset 0 0 0 1px var(--hairline);
}

.gate h2 {
  font-family: var(--rounded);
  margin: 0 0 var(--s);
}

.gate p {
  margin: 0;
  color: var(--ink-soft);
}

/* Cosmetic only. The refusal itself is in membership.js, which wraps the three
   write methods on the CloudKit client — this just stops a member being offered
   a button that would tell them no. Controls drawn in JavaScript have no shared
   class yet, so this list is the obvious ones rather than all of them. */
.is-readonly #import,
.is-readonly #weigh,
.is-readonly #pick,
.is-readonly #trips-pick,
.is-readonly #edit-walk,
.is-readonly #walk-pack,
.is-readonly #walk-plans,
.is-readonly #sweep { display: none; }

/* ---- an expired sign-in -------------------------------------------------- */

/* Sits above the work rather than replacing it, because the work is still
   there. Accent rather than a warning colour: nothing has gone wrong, a clock
   has run out. */
/* RULE: ANYTHING GIVEN A `display` NEEDS ITS OWN `[hidden]`.
   The browser hides `hidden` elements with a `display: none` of the very
   lowest priority, and a class rule with a `display` in it wins — so a panel
   styled `display: flex` is visible whatever the JavaScript sets. This
   stylesheet has always handled that one element at a time; the banner needs
   the same line, and without it sat on screen permanently with a button
   nothing had wired up. */
.expired[hidden] { display: none; }

.expired {
  display: flex;
  gap: var(--l);
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  background: var(--surface);
  border-radius: var(--card-radius);
  padding: var(--m) var(--l);
  margin-bottom: var(--l);
  box-shadow: inset 0 0 0 2px var(--accent);
}

.expired h2 {
  font-family: var(--rounded);
  margin: 0 0 var(--s);
}

.expired p {
  margin: 0;
  color: var(--ink-soft);
}

.expired-note:empty { display: none; }

.expired-note {
  margin-top: var(--s);
  color: var(--accent);
}

/* ---- the one-off sweep --------------------------------------------------- */

.strays { margin-top: var(--l); }
.strays button { margin-top: var(--s); }

#strays-list {
  margin: var(--s) 0 0;
  padding: var(--m);
  background: var(--control);
  border-radius: var(--card-radius);
  font-family: var(--mono);
  white-space: pre-wrap;
}

#strays-list[hidden],
#strays-remove[hidden] { display: none; }
