/* Forest Breach — web app. Engraving/woodcut identity: paper, ink, fleurons. */

:root {
  --paper: #f2f1ea;
  --ink: #1c1c1a;
  --faint: #6b6a62;
  --rule: #1c1c1a;
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
  background: #2a2a28;
  color: var(--ink);
  font-family: "IM Fell English", Georgia, "Times New Roman", serif;
  font-size: 17px;
  line-height: 1.45;
}

#app {
  max-width: 760px;
  margin: 0 auto;
  min-height: 100dvh;
  background: var(--paper);
  display: flex;
  flex-direction: column;
  box-shadow: 0 0 40px rgba(0, 0, 0, 0.5);
}

main {
  flex: 1;
  padding: 12px 16px 90px;
}

/* masthead */
.mh {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  border-bottom: 2px solid var(--rule);
  padding: 10px 16px 4px;
  gap: 8px;
}
.mh .title {
  font-size: 22px;
  font-weight: 700;
  letter-spacing: 3px;
  white-space: nowrap;
}
.mh .side {
  font-size: 13px;
  color: var(--ink);
}
.fleuron {
  text-align: center;
  font-size: 13px;
  letter-spacing: 4px;
  padding: 3px 0;
  user-select: none;
}

/* sections */
.sh {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 1.5px;
  border-bottom: 1px solid var(--rule);
  margin: 18px 0 6px;
  text-transform: uppercase;
}
/* a fight's rounds: a heading per round, one row per action */
.rh {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  margin: 14px 0 4px;
}
.rh::after {
  content: "";
  display: block;
  width: 3.5em;
  border-bottom: 1px solid var(--rule);
  margin-top: 2px;
}
.rline {
  margin: 0 0 4px;
  padding-left: 10px;
  text-indent: -10px;
}
.jsb {
  display: flex;
  justify-content: space-between;
  gap: 8px;
}
.faint {
  color: var(--faint);
  font-style: italic;
}
.small {
  font-size: 14px;
}

/* the objectives a plot step asks for, one per row */
.objectives {
  margin: 4px 0 6px;
  padding-left: 18px;
}
.objectives li {
  margin: 2px 0;
}

/* journal entries */
.entry {
  padding: 8px 2px;
  border-bottom: 1px solid var(--rule);
  cursor: pointer;
}
.entry:active {
  background: rgba(28, 28, 26, 0.06);
}
.entry .glyph {
  font-weight: 700;
  margin-right: 6px;
}
.entry.milestone .glyph {
  font-size: 18px;
}
.dropcap::first-letter {
  font-size: 200%;
  font-weight: 700;
  float: left;
  line-height: 1;
  padding-right: 4px;
}

/* buttons & forms */
button,
.btn {
  font-family: inherit;
  font-size: 16px;
  background: var(--paper);
  color: var(--ink);
  border: 1.5px solid var(--ink);
  padding: 8px 14px;
  cursor: pointer;
  letter-spacing: 0.5px;
}
button.primary {
  background: var(--ink);
  color: var(--paper);
}
button:disabled {
  opacity: 0.4;
  cursor: default;
}
input,
select {
  font-family: inherit;
  font-size: 16px;
  background: var(--paper);
  color: var(--ink);
  border: 1.5px solid var(--ink);
  padding: 8px 10px;
  width: 100%;
}
label {
  display: block;
  font-size: 13px;
  letter-spacing: 1px;
  text-transform: uppercase;
  margin: 12px 0 4px;
}

/* cards */
.card {
  border: 1.5px solid var(--ink);
  padding: 10px 12px;
  margin-bottom: 10px;
}
.card.tappable {
  cursor: pointer;
}
.card.tappable:active {
  background: rgba(28, 28, 26, 0.06);
}
.card.downed {
  opacity: 0.55;
  filter: grayscale(0.4);
}

.bar {
  font-family: ui-monospace, monospace;
  font-size: 13px;
  letter-spacing: -1px;
  white-space: nowrap;
}

/* meters — vigor and experience. A drawn track reads as a measure; the block
   characters this replaced merged into a solid slab at full fill. */
.meter {
  display: inline-block;
  vertical-align: middle;
  width: 100%;
  height: 10px;
  border: 1.5px solid var(--ink);
  padding: 1.5px;
}
.meter > i {
  display: block;
  height: 100%;
  background: var(--ink);
}
/* label | meter | reading — the meter takes whatever the two ends leave, but
   never less than 64px. A roster card can carry a long string of conditions
   beside the bar, and without a floor the track collapses to a few pixels. */
.gauge {
  display: grid;
  grid-template-columns: auto minmax(64px, 1fr) auto;
  align-items: center;
  gap: 10px;
  margin: 4px 0;
}
.gauge > :last-child {
  min-width: 0;
  text-align: right;
}

/* the four stats, one column each */
.statgrid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  text-align: center;
  gap: 2px 6px;
  margin: 10px 0 14px;
}
.statgrid .name {
  font-size: 12px;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--faint);
}
.statgrid .value {
  font-size: 24px;
  line-height: 1.1;
}
.statgrid .note {
  font-size: 11px;
  line-height: 1.3;
  color: var(--faint);
  font-style: italic;
}
/* the role's own stat carries the weight */
.statgrid .name.lead,
.statgrid .value.lead {
  color: var(--ink);
  font-weight: 700;
}

/* bottom nav — the device nav strip, made thumbable */
nav {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  max-width: 760px;
  margin: 0 auto;
  background: var(--paper);
  border-top: 2px solid var(--rule);
  display: flex;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding: 0 4px env(safe-area-inset-bottom);
}
nav::-webkit-scrollbar {
  display: none;
}
/* `1 1 auto`, not `1 0 auto`: five hubs and a gear must FIT, and the strip only scrolled
   before because fourteen labels never could. A shrinkable slot keeps the gear on screen at
   320px instead of parking it 40px past the right edge, where nothing but a horizontal drag
   would ever find it. */
nav a {
  flex: 1 1 auto;
  min-width: 0;
  text-align: center;
  padding: 12px 6px;
  font-size: 13px;
  letter-spacing: 0.5px;
  color: var(--ink);
  text-decoration: none;
  white-space: nowrap;
}
nav a.on {
  background: var(--ink);
  color: var(--paper);
}
/* settings — a glyph, so it costs a sliver of the strip rather than a word slot. Stuck to
   the right edge so that even if a long label or a very narrow screen does force the strip
   to scroll, the gear stays where it can be tapped. */
nav a.gear {
  flex: 0 0 auto;
  padding: 12px 10px;
  font-size: 15px;
  letter-spacing: 0;
  position: sticky;
  right: 0;
  background: var(--paper);
}
nav a.gear.on {
  background: var(--ink);
}

/* the hub's own sections, under its masthead — quieter than the root strip */
.subnav {
  display: flex;
  flex-wrap: wrap;
  gap: 2px 14px;
  padding: 7px 16px;
  border-bottom: 1px solid var(--rule);
  font-size: 12px;
  letter-spacing: 1px;
}
.subnav a,
.subnav .off {
  color: var(--faint);
  text-decoration: none;
  white-space: nowrap;
}
.subnav a.on {
  color: var(--ink);
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 3px;
}
/* a section you can't reach from here — shown, but plainly shut */
.subnav .off {
  opacity: 0.45;
  cursor: default;
  text-decoration: line-through;
}

/* map */
.map-wrap {
  border: 1.5px solid var(--ink);
  overflow: hidden;
}
.map-wrap svg {
  display: block;
  width: 100%;
  height: auto;
  touch-action: pan-x pan-y;
}

/* A pannable map is a window onto a bigger sheet: fixed height, the SVG at its
   natural size riding a transform. touch-action:none so a finger drags the map
   instead of scrolling the page. */
.map-wrap.pannable {
  position: relative;
  height: min(70vh, 560px);
  touch-action: none;
  cursor: grab;
}
.map-wrap.pannable.grabbing {
  cursor: grabbing;
}
.map-wrap.pannable svg {
  width: auto;
  height: auto;
  transform-origin: 0 0;
  touch-action: none;
  user-select: none;
}
.map-ctl {
  position: absolute;
  right: 8px;
  bottom: 8px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.map-ctl button {
  width: 38px;
  height: 38px;
  padding: 0;
  font-size: 18px;
  line-height: 1;
  background: var(--paper);
  border: 1.5px solid var(--ink);
  color: var(--ink);
  opacity: 0.92;
  cursor: pointer;
}

/* The map key. Each entry is a swatch the map itself drew, then what it means. */
.key {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 4px 14px;
  margin: 8px 0;
}
.key span {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 13px;
  font-style: italic;
  color: var(--faint);
}
.key svg {
  display: block;
  flex: none;
  border: 1px solid var(--rule);
}

/* odds */
.mod {
  display: flex;
  justify-content: space-between;
  font-size: 14px;
  padding: 2px 0;
}
/* The odds panel under the order form. It is always on screen and re-reads
   itself whenever the form changes, so it gets a rule to mark it off from the
   controls, and fades while a fresh reading is on the way. */
.odds-live {
  border-top: 1px solid var(--rule);
  padding-top: 6px;
  min-height: 44px;
}
.odds-live.stale {
  opacity: 0.45;
}
.warn {
  border: 1.5px solid var(--ink);
  padding: 8px 12px;
  font-weight: 700;
  margin: 10px 0;
}

/* the roads out of here — one row per landmark you can travel to */
.road {
  border-bottom: 1px solid var(--rule);
  padding: 6px 0;
}
.road:last-of-type {
  border-bottom: 0;
}
.road p {
  margin: 2px 0 0;
}
.roads-more summary {
  font-size: 14px;
  cursor: pointer;
  padding: 6px 0;
}
/* the shade block reads as weight, so it needs to sit tight against the word */
.road-cond {
  font-family: ui-monospace, monospace;
  font-size: 13px;
  white-space: nowrap;
}

/* the last order, offered again — greyed when it cannot be given today */
.repeat-off {
  opacity: 0.55;
}

.repeat-blocked {
  font-style: italic;
}

/* the plan */
.plan-toggle {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  text-transform: none;
  letter-spacing: normal;
  margin: 0 0 12px;
}
.plan-toggle input {
  width: auto;
}
/* redacted guide bars */
.redact {
  background: var(--ink);
  color: var(--ink);
  user-select: none;
  letter-spacing: 2px;
}

.pill {
  display: inline-block;
  border: 1px solid var(--ink);
  font-size: 12px;
  padding: 1px 7px;
  margin: 2px 4px 2px 0;
  letter-spacing: 1px;
}

.center {
  text-align: center;
}
.mt {
  margin-top: 14px;
}

/* join screen */
.join {
  min-height: 80dvh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 24px;
  text-align: center;
}
.join h1 {
  font-size: 30px;
  letter-spacing: 5px;
  font-weight: 700;
}

@media (max-width: 480px) {
  body {
    font-size: 16px;
  }
  .mh .title {
    font-size: 18px;
    letter-spacing: 2px;
  }
  .mh .side {
    font-size: 11px;
  }
}

/* guild halls fold shut so a town isn't a wall of patterns */
details.hall > summary {
  list-style: none;
  cursor: pointer;
  /* the whole header line is the tap target, edge to edge of the card */
  margin: -10px -12px;
  padding: 10px 12px;
}
details.hall > summary::-webkit-details-marker {
  display: none;
}
details.hall > summary p:last-child {
  margin-bottom: 0;
}
details.hall > summary .jsb b::before {
  content: "▸ ";
}
details.hall[open] > summary .jsb b::before {
  content: "▾ ";
}
details.hall[open] > summary {
  margin-bottom: 0;
}

/* expandable gear rows — the appraisal ledger */
details.gear > summary {
  list-style: none;
  cursor: pointer;
}
details.gear > summary::-webkit-details-marker {
  display: none;
}
.gear-lore {
  margin: 2px 0 8px 12px;
  border-left: 2px solid #ccc;
  padding-left: 8px;
}

/* A priced row — market, your goods, the armory, repairs. The name takes up
   the slack and the buttons sit in a fixed column at the right, so a run of
   rows reads as one straight column instead of a staircase. Prices go in the
   name line rather than the button label; that is what keeps every button the
   same width. */
.trade {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  padding: 6px 2px;
  border-bottom: 1px solid var(--rule);
}
.trade .goods {
  min-width: 0;
}
.trade .acts {
  display: flex;
  gap: 6px;
  flex: none;
}
.trade .acts button {
  min-width: 4.6em;
  padding: 6px 10px;
  text-align: center;
}
/* the armory's rows fold open into an appraisal, so the row is a summary */
details.gear > summary.trade {
  border-bottom: 1px solid var(--rule);
}
/* Buying by the handful. The stepper takes over a market row's button column;
   the total sits on its own line above the controls so the row keeps its width
   instead of stretching into a staircase. */
.trade .acts.stepping {
  flex: 1 1 auto;
}
.trade .acts.stepping > button {
  display: none;
}
.stepper {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: flex-end;
  gap: 6px;
  width: 100%;
}
.trade .acts .stepper button {
  min-width: 0;
  padding: 6px 8px;
}
.stepper .count {
  min-width: 2em;
  text-align: center;
}
.stepper .steptotal {
  flex-basis: 100%;
  text-align: right;
}
