/* ══════════════════════════════════════════════════════════════════════════
   Bean Counter — application layer.

   organic.css (the vendored Organic design system) is the source of truth for
   every colour, font, space, radius and shadow. Nothing here introduces a new
   token; this file only carries layout scaffolding, native-control theming and
   the responsive rules the design implies but a static canvas cannot express.
   ══════════════════════════════════════════════════════════════════════════ */

html { -webkit-text-size-adjust: 100%; }
body { min-height: 100vh; }

/* Panels, the legal dialog and the surfaces are all shown and hidden through
   the hidden attribute, and every one of them carries a class that sets its
   own display. Without this the attribute silently loses. */
[hidden] { display: none !important; }

/* — the panel grid. min() keeps the designed 378px column on every screen wide
     enough for it, and prevents a horizontal overflow on narrower ones. — */
.panels {
  display: grid; gap: 16px; align-items: start;
  grid-template-columns: repeat(auto-fit, minmax(min(378px, 100%), 1fr));
  font-family: var(--font-body); color: var(--color-text);
}
.panel-wide { grid-column: 1 / -1; }
.panel { padding: 22px; gap: 14px; }
.panel-kicker { font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--color-accent-700); }
.panel-title { font-family: var(--font-heading); font-size: 21px; line-height: 1.15; }
.panel-lede { font-size: 12px; color: var(--color-neutral-700); margin-top: -6px; }

/* — tiles used across summary, break-even, ROI and season panels — */
.tile { background: var(--color-bg); border-radius: 20px; padding: 13px 14px; text-align: center; }
.tile-val { font-family: var(--font-heading); font-size: 20px; }
.tile-lbl { font-size: 9.5px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--color-neutral-700); margin-top: 4px; }

/* — sliders. accent-color gives the design's terracotta fill on the native
     control, exactly as the canvas specifies. — */
.slider-row { display: grid; grid-template-columns: 1fr auto; gap: 4px 12px; align-items: baseline; }
.slider-name { font-size: 13.5px; font-weight: 600; display: flex; align-items: center; gap: 7px; }
/* Nina's first finding: the figure was display-only, so the sixteen inputs
   could only be dragged. It is now a field. At rest it carries no chrome and
   looks exactly like the label it replaced; hover and focus reveal that it
   takes typing. */
.slider-val {
  font-size: 13px; font-family: var(--font-heading); color: var(--color-accent-700);
  /* 10ch was 76px and "R500 000" is 78, so the widest figure the revenue
     slider can produce was clipped by two pixels at every screen size. */
  text-align: right; width: 11ch; min-height: 28px; padding: 3px 9px;
  border: 1px solid transparent; border-radius: 999px;
  background: transparent; cursor: text;
}
.slider-val:hover { border-color: var(--color-divider); background: var(--color-bg); }
.slider-val:focus { outline: none; border-color: var(--color-accent); background: var(--color-bg); }
/* What a percentage costs in rand, said directly under the slider that sets
   it. A merchant rate of 3% means nothing on its own. */
.slider-note {
  grid-column: 1 / -1; margin-top: 2px;
  font-size: 11px; line-height: 1.45; color: var(--color-accent-800);
  background: var(--color-accent-200); border-radius: 12px; padding: 6px 10px;
}

/* — the staff mix. One row per role: how many, what each earns, and the hours
     that decide whether that salary is lawful. — */
.staff-row {
  display: grid; gap: 6px; padding: 12px 0;
  border-bottom: 1px solid var(--color-divider);
}
.staff-row:last-of-type { border-bottom: 0; }
.staff-role { font-size: 13.5px; font-weight: 600; }
.staff-fields { display: grid; gap: 8px; grid-template-columns: repeat(3, minmax(0, 1fr)); }
.staff-field { display: grid; gap: 3px; min-width: 0; }
.staff-field > span { font-size: 10.5px; color: var(--color-neutral-700); }
.staff-field .input {
  min-height: 44px; text-align: center; padding-inline: 6px;
  font-family: var(--font-heading); background: var(--color-bg);
}
.staff-note { font-size: 11px; line-height: 1.45; color: var(--color-neutral-700); }
.staff-note-warn {
  color: var(--color-accent-800); background: var(--color-accent-200);
  border-radius: 12px; padding: 6px 10px;
}
.staff-total {
  font-size: 12px; color: var(--color-accent-700); font-weight: 600;
  background: var(--color-bg); border-radius: 14px; padding: 8px 12px;
}
.staff-total-warn { color: var(--color-accent-800); background: var(--color-accent-200); }

/* — a working-out box: the cups-a-day helper behind revenue, and the computed
     lines under the card fees and the ROI setup split. — */
.helper {
  background: var(--color-bg); border-radius: 18px; padding: 11px 13px;
  display: grid; gap: 8px; margin: 2px 0 4px;
}
.helper-title { font-size: 11.5px; font-weight: 600; color: var(--color-accent-700); }
.helper-fields { display: grid; gap: 8px; grid-template-columns: repeat(2, minmax(0, 1fr)); }
.helper-fields .input {
  min-height: 44px; text-align: center; padding-inline: 6px;
  font-family: var(--font-heading); background: var(--color-surface);
}
.helper-calc { font-size: 11.5px; line-height: 1.5; color: var(--color-neutral-700); }

/* — jump row. The Plan tab is thousands of pixels on a phone and the Model tab
     is six panels; this gets you to one without scrolling past the rest. — */
.jump-row {
  display: flex; gap: 6px; flex-wrap: wrap; align-items: center;
  padding: 2px 0 2px;
}
.jump-lbl {
  font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--color-neutral-700); margin-right: 2px;
}
.jump-chip {
  border: 1px solid var(--color-accent-300); cursor: pointer;
  background: var(--color-accent-100); color: var(--color-accent-700);
  font-family: var(--font-body); font-size: 12px; font-weight: 600;
  border-radius: 999px; padding: 7px 13px; min-height: 34px;
}
.jump-chip:hover { background: var(--color-accent-200); }
.jump-chip:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }

/* the sticky header would otherwise sit on top of whatever a chip jumps to */
.panels > .card { scroll-margin-top: 86px; }

/* ── skeleton ──────────────────────────────────────────────────────────────
   The only wait this app has is its own script: 114KB of app.js has to arrive
   and run before #app-panels holds anything. There is no request and no API
   behind this, so the shapes are fixed rather than driven by a loading state —
   they are replaced outright the moment panelsHTML writes. Styled here so they
   appear with the stylesheet, which lands well before the script. */
.sk-card {
  background: var(--color-surface); border-radius: var(--radius-lg, 28px);
  padding: 22px; display: grid; gap: 12px; box-shadow: var(--shadow-sm);
}
.sk-line, .sk-tile, .sk-block {
  background: var(--color-neutral-300); border-radius: 999px; height: 13px;
}
.sk-kicker { height: 9px; width: 34%; }
.sk-title { height: 20px; width: 62%; }
.sk-big { height: 40px; width: 78%; border-radius: 14px; }
.sk-bar { height: 14px; width: 100%; }
.sk-block { height: 74px; border-radius: 22px; }
.sk-tiles { display: grid; gap: 10px; grid-template-columns: repeat(auto-fit, minmax(min(150px, 100%), 1fr)); }
.sk-tiles-4 { grid-template-columns: repeat(auto-fit, minmax(min(130px, 100%), 1fr)); }
.sk-tile { height: 68px; border-radius: 20px; }
/* announced once by a screen reader; invisible, because the shapes above are
   the visible half of the same message */
.sk-status {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; margin: 0;
}
.sk-card .sk-line, .sk-card .sk-tile, .sk-card .sk-block {
  animation: sk-pulse 1.4s ease-in-out infinite;
}
@keyframes sk-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.45; } }
@media (prefers-reduced-motion: reduce) {
  .sk-card .sk-line, .sk-card .sk-tile, .sk-card .sk-block { animation: none; opacity: 0.8; }
}

/* ── tooltips ──────────────────────────────────────────────────────────────
   For the controls that carry meaning without carrying text: the six tax
   switches, whose only state cue is a colour and a knob position. CSS and a
   data attribute, so nothing is added to the script and nothing is injected
   into the page. Shown on hover and on keyboard focus, both.
   The switch already uses ::after for its 44px tap target, so the bubble is
   ::before throughout. */
[data-tip] { position: relative; }
[data-tip]::before {
  content: attr(data-tip);
  position: absolute; bottom: calc(100% + 9px); left: 50%; translate: -50% 0;
  z-index: 40; pointer-events: none;
  background: var(--color-neutral-900); color: var(--color-neutral-100);
  font-family: var(--font-body); font-size: 11.5px; line-height: 1.45;
  font-weight: 400; letter-spacing: normal; text-transform: none;
  padding: 7px 10px; border-radius: 12px; width: max-content; max-width: 210px;
  white-space: normal; text-align: center;
  opacity: 0; visibility: hidden; transition: opacity .14s ease;
}
[data-tip]:hover::before,
[data-tip]:focus-visible::before { opacity: 1; visibility: visible; }
@media (prefers-reduced-motion: reduce) { [data-tip]::before { transition: none; } }
/* near the left edge the bubble would be clipped by the card */
.slider-name [data-tip]::before { left: 0; translate: 0 0; text-align: left; }

/* Nina: 86px cells put three months across a phone, leaving each slider about
   50px to cover a 40-160% range. Two per row doubles the travel. */
.season-grid { display: grid; gap: 8px; grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (min-width: 560px) { .season-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 960px) { .season-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (min-width: 1400px) { .season-grid { grid-template-columns: repeat(6, minmax(0, 1fr)); } }
.slider-hint { font-size: 11px; color: var(--color-neutral-700); grid-column: 1 / -1; margin-top: -2px; }
input[type="range"] {
  grid-column: 1 / -1; width: 100%; height: 24px; margin: 0;
  accent-color: var(--color-accent); cursor: pointer; background: transparent;
}
input[type="range"]:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; border-radius: 999px; }
/* The twelve month sliders were set to 20px inline, below the 24px target
   minimum. The browser draws the track at a fixed thickness and centres it, so
   raising the box only grows the hit area. */
.season-cell input[type="range"] { height: 24px; }
.dot { width: 9px; height: 9px; border-radius: 50%; flex: none; }

/* — progress bars — */
.bar-track { height: 14px; border-radius: 999px; background: var(--color-neutral-300); overflow: hidden; }
.bar-fill { height: 100%; border-radius: 999px; transition: width .5s cubic-bezier(.4, 0, .2, 1); }
.bar-axis { display: flex; justify-content: space-between; font-size: 11px; color: var(--color-neutral-700); }

/* — native select rendered as the design's pill input — */
select.input {
  cursor: pointer; appearance: none; -webkit-appearance: none;
  padding-right: 34px;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%238c491a' stroke-width='2.75' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 13px center;
}
input[type="number"] { appearance: textfield; -webkit-appearance: textfield; }

/* — the tax switch — */
/* The switch is drawn 40x22 exactly as designed. WCAG 2.2 asks for a 24px
   minimum target, so an invisible pseudo-element extends the tappable area to
   44x44 around it. Nothing moves: the visual, the layout and the knob travel
   are untouched, only the region that accepts a finger grows. */
.switch { width: 40px; height: 22px; border-radius: 999px; flex: none; cursor: pointer; position: relative; transition: background .2s; }
.switch::after { content: ""; position: absolute; inset: -11px -2px; border-radius: 999px; }
.switch-knob { position: absolute; top: 3px; left: 3px; width: 16px; height: 16px; border-radius: 50%; background: var(--color-bg); transition: transform .2s; }
.switch:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }

/* — tables that must scroll rather than push the page sideways — */
.scroll-x { overflow-x: auto; }

/* ── layout ───────────────────────────────────────────────────────────────
   One set of markup, two shapes. Below the breakpoint the four tabs are a bar
   fixed to the bottom of the screen, the way a phone app works. At tablet width
   and up the same nav becomes a sidebar and the panels flow into as many
   columns as the width allows. Nothing is duplicated: the buttons are the same
   elements, only their box changes. */

.app { background: var(--color-bg); color: var(--color-text); font-family: var(--font-body); }

.bean { width: 27px; height: 27px; flex: none; display: block; }

.app-nav { background: var(--color-surface); }
.app-nav button {
  border: 0; cursor: pointer; background: transparent;
  color: var(--color-neutral-700); font-family: var(--font-body);
  display: flex; align-items: center; border-radius: 18px;
}
.app-nav button svg { width: 22px; height: 22px; flex: none; }
.app-nav button span { font-size: 10.5px; letter-spacing: 0.02em; }
.app-nav button[aria-selected="true"] { color: var(--color-accent-700); background: var(--color-accent-100); }
.app-nav button[aria-selected="true"] span { font-weight: 700; }
.app-nav button:focus-visible { outline: 2px solid var(--color-accent); outline-offset: -2px; }

.app-head {
  position: sticky; top: 0; z-index: 20;
  display: flex; align-items: center; gap: 10px;
  padding: 12px 16px; background: var(--color-bg);
}
/* Nina: on a phone the name Bean Counter appeared nowhere on screen. The header
   showed only the section — Today, Model, Tax setup — so someone opening it from
   their home screen saw a bean, a section name and some numbers. A kicker above
   the section title fixes it, and matches the kicker used on every panel. The
   desktop sidebar already carries the name, so it is hidden there. */
.app-head-app {
  font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--color-accent-700); margin-bottom: 1px;
}
.app-head-title { font-family: var(--font-heading); font-size: 17px; line-height: 1.1; }
.app-head-sub { font-size: 10.5px; color: var(--color-neutral-700); }
#app-chip { flex: none; }

.app-pad { padding: 2px 12px 8px; }

/* ── phone: the nav is a bar fixed to the bottom ──────────────────────── */
@media (max-width: 759px) {
  .app-nav {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
    display: grid; grid-template-columns: repeat(4, 1fr); gap: 2px;
    padding: 8px 8px max(8px, env(safe-area-inset-bottom));
    border-top: 1px solid var(--color-divider);
    border-radius: 22px 22px 0 0; box-shadow: var(--shadow-md);
  }
  .app-nav-brand { display: none; }
  .app-nav button { flex-direction: column; gap: 4px; padding: 7px 2px 5px; min-height: 52px; }
}

/* ── tablet and up: the nav is a sidebar ──────────────────────────────── */
@media (min-width: 760px) {
  .app {
    display: grid; grid-template-columns: clamp(190px, 18vw, 240px) minmax(0, 1fr);
    min-height: 100vh; align-items: start;
  }
  .app-nav {
    position: sticky; top: 0; height: 100vh;
    display: flex; flex-direction: column; gap: 4px;
    padding: 22px 12px; border-right: 1px solid var(--color-divider);
  }
  .app-nav-brand {
    display: flex; align-items: center; gap: 10px;
    padding: 4px 10px 20px; font-family: var(--font-heading); font-size: 17px;
  }
  .app-nav button {
    flex-direction: row; justify-content: flex-start; gap: 12px;
    padding: 11px 14px; min-height: 46px;
  }
  .app-nav button span { font-size: 14px; }
  .app-head-app { display: none; }
  .app-head { padding: 20px 28px 14px; }
  .app-head-title { font-size: 24px; }
  .app-head-sub { font-size: 12px; }
  .app-pad { padding: 0 28px 10px; }
  .app-tabbar-spacer { display: none; }
  .app-footer { padding: 34px 28px 40px; }
  .app-footer-inner { max-width: 1100px; }
}

/* Link-styled buttons. Restored: a botched edit dropped this rule, which left
   the footer's policy links wearing the browser's default grey button face —
   peach text on #efefef inside a dark footer, 1.32:1. The sweep caught it. */
.linkbtn {
  border: 0; background: transparent; cursor: pointer;
  font-family: var(--font-body); text-decoration: underline; text-underline-offset: 3px;
}

/* ── the footer, built to the supplied design ──────────────────────────────
   Dark ground, the IFTFC wordmark in the light accent, the company name
   letterspaced beneath it, then contact, a hairline rule, the two policy links
   and the disclaimer. Stacked and left-aligned rather than the split alignment
   the wide layout used: at phone width a right-aligned contact line reads as a
   mistake rather than a choice. Every opacity here was checked against the dark
   ground; the lowest lands at 5.16:1, comfortably inside AA. */
.app-footer {
  background: var(--color-neutral-900); color: var(--color-neutral-200);
  padding: 30px 20px 34px; margin-top: 20px;
}
.app-footer-brand { font-family: var(--font-heading); font-size: 30px; line-height: 1.1; color: var(--color-accent-300); }
.app-footer-full { font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase; opacity: 0.78; margin-top: 8px; }
.app-footer-services { font-size: 13px; line-height: 1.7; opacity: 0.85; margin: 20px 0 0; }
.app-footer-contact { font-size: 13px; line-height: 1.7; opacity: 0.85; margin: 2px 0 0; }
/* WCAG 2.2 SC 2.5.8 exempts a link sitting inline in a sentence, but 16px is
   still a mean thing to ask a thumb for. Padding takes it to 24 without moving
   the line. */
.app-footer-contact a { color: var(--color-accent-300); display: inline-block; padding: 4px 0; }
.app-footer-contact a:hover { color: var(--color-accent-200); }
.app-footer-rule { height: 1px; background: rgba(245, 234, 216, 0.18); margin: 22px 0 14px; }
.app-footer-links { display: flex; gap: 26px; flex-wrap: wrap; align-items: center; }
.app-footer-links .linkbtn { font-size: 13px; color: var(--color-accent-300); min-height: 44px; padding: 0; }
.app-footer-links .linkbtn:hover { color: var(--color-accent-200); }
.app-footer-links .linkbtn:focus-visible { outline: 2px solid var(--color-accent-300); outline-offset: 3px; border-radius: 4px; }
.app-footer-copy { font-size: 12.5px; opacity: 0.62; margin-top: 6px; }
.app-footer-note { font-size: 11.5px; line-height: 1.7; opacity: 0.6; font-style: italic; margin: 20px 0 0; }

/* ── the tab bar never covers the end of the page ──────────────────────────
   On a phone the bar is fixed, so it takes no room in the flow and the last
   thing on the page sits underneath it. Richard caught exactly that on a real
   handset: the final card was hidden behind the bar. The spacer reserves the
   bar's real measured height, which app.js writes into --tabbar-h on load and
   on resize, so it stays correct whatever the safe-area inset or text size.
   At sidebar width the nav sits beside the content, so the spacer collapses. */
.app-tabbar-spacer { height: calc(var(--tabbar-h, 92px) + 8px); }

/* ── remaining responsive ─────────────────────────────────────────────── */

@media (max-width: 560px) {
  .panel { padding: 18px 16px; }
}

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

/* Printing the model is a reasonable thing to want; the chrome is not. */
@media print {
  .app-nav, .app-tabbar-spacer, .app-footer-links { display: none; }
  .app { display: block; }
  .panels { display: block; }
  .card { break-inside: avoid; box-shadow: none; }
}
