/* ---------------------------------------------------------------------------
   Tokens.

   Light is the default and the operating system is not consulted. Somebody
   working at night with a dark desktop still expects an invoice to look like a
   document; dark is a choice, taken with the button in the sidebar, and it is
   remembered. The attribute is set before this sheet loads so the page is
   painted once rather than flashing light and repainting.

   Spacing is a scale and rhythm comes from gap on containers, never from
   margins on the things inside them. Two components meeting used to produce
   whatever the sum of their margins happened to be, which is why the same pair
   looked different in different places.
   --------------------------------------------------------------------------- */

:root {
  --s1: .25rem;
  --s2: .5rem;
  --s3: .75rem;
  --s4: 1rem;
  --s5: 1.5rem;
  --s6: 2rem;
  --s7: 3rem;

  --t-xs: .75rem;
  --t-sm: .8125rem;
  --t-md: .875rem;
  --t-base: .9375rem;
  --t-lg: 1.0625rem;
  --t-xl: 1.3125rem;
  --t-2xl: 1.75rem;

  --r-sm: 8px;
  --r-md: 10px;
  --r-lg: 14px;
  --r-full: 999px;

  /* The canvas is tinted enough to be a canvas. At #f7f8fa it was within a
     whisker of the white cards on it, so nothing separated from anything and the
     whole page read as one flat white sheet. Surfaces are white and sit on top of
     this; the difference is what gives the layout its shape. */
  --bg: #eaedf3;
  --surface: #ffffff;
  --sunk: #f3f5f9;
  --ink: #101828;
  --muted: #5d6b82;
  --faint: #8b97a9;
  --line: #dfe3ea;
  --line-strong: #c6cdd8;

  --accent: #4f46e5;
  --accent-hover: #4338ca;
  --accent-ink: #ffffff;
  --accent-soft: #eef2ff;
  --accent-line: #c7d2fe;

  --bad: #d92d20;
  --bad-soft: #fef3f2;
  --bad-line: #fecdca;
  --ok: #067647;
  --ok-soft: #ecfdf3;
  --ok-line: #abefc6;
  --wait: #b54708;
  --wait-soft: #fffaeb;
  --wait-line: #fedf89;

  --shadow-1: 0 1px 3px rgba(16, 24, 40, .06), 0 1px 2px rgba(16, 24, 40, .04);
  --shadow-2: 0 8px 24px -6px rgba(16, 24, 40, .12), 0 2px 6px -2px rgba(16, 24, 40, .06);
  --ring: 0 0 0 4px var(--accent-soft);

  --select-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5' fill='none' stroke='%235d6b82' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");

  color-scheme: light;
}

:root[data-theme="dark"] {
  --bg: #0c0e12;
  --surface: #14181f;
  --sunk: #1b212b;
  --ink: #f0f2f5;
  --muted: #94a0b4;
  --faint: #6b7688;
  --line: #232935;
  --line-strong: #333c4c;

  --accent: #8b85ff;
  --accent-hover: #a29dff;
  --accent-ink: #0c0e12;
  --accent-soft: #1c1e33;
  --accent-line: #35386b;

  --bad: #ff8578;
  --bad-soft: #2a1613;
  --bad-line: #59211a;
  --ok: #5ed69a;
  --ok-soft: #10231a;
  --ok-line: #1d4634;
  --wait: #f0bb62;
  --wait-soft: #261c0c;
  --wait-line: #4a3616;

  --shadow-1: 0 1px 2px rgba(0, 0, 0, .4);
  --shadow-2: 0 4px 12px -2px rgba(0, 0, 0, .5);

  --select-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5 6 6.5 11 1.5' fill='none' stroke='%2394a0b4' stroke-width='1.75' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  --ring: 0 0 0 4px color-mix(in srgb, var(--accent) 22%, transparent);

  color-scheme: dark;
}

* { box-sizing: border-box; }

/* Everything here is sized in rem, so moving the root moves the whole interface together. On a
   large monitor the default sixteen pixels leaves an application built for a laptop sitting in the
   middle of it, which is what "small" means on a 2K screen: not a scaling bug, just a layout that
   never grew. Two steps rather than a smooth ramp, because a fluid root makes every rounded value
   land somewhere different at every width. */
/* The gutter is reserved whether or not there is anything to scroll, so a short list and a
   long one put the page in the same place. Without it every navigation between them nudged the
   whole layout sideways by half a scrollbar - small enough to read as a rendering fault rather
   than as the window changing. */
html { font-size: 100%; scrollbar-gutter: stable; }

@media (min-width: 1600px) { html { font-size: 106.25%; } }
@media (min-width: 2200px) { html { font-size: 112.5%; } }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI Variable Text", "Segoe UI",
    system-ui, Roboto, "Helvetica Neue", sans-serif;
  font-size: var(--t-base);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  font-variant-numeric: tabular-nums;
}

.booting { padding: var(--s7) var(--s4); text-align: center; color: var(--muted); }

/* --------------------------------------------------------------------------
   Shell
   -------------------------------------------------------------------------- */

.app { display: flex; flex-direction: column; min-height: 100vh; }

/* Full width, so the rule under it reaches both edges of the window. What sits inside is held
   to the same column as the page, so the name lines up with the content beneath it. */
.topbar {
  position: sticky; top: 0; z-index: 40;
  background: var(--surface); border-bottom: 1px solid var(--line);
}

.bar-inner {
  /* The same column as the page below, so the name sits over the heading rather than 36px to
     the left of it. The cap counts this element's own padding while the page's does not, so
     the padding is added back rather than the two numbers being written to look equal. */
  max-width: calc(72rem + var(--s5) * 2); margin: 0 auto; padding: 0 var(--s5);
  display: flex; align-items: center; gap: var(--s5); min-height: 3.5rem;
}

.mark {
  display: flex; align-items: center; gap: var(--s2);
  font-size: var(--t-lg); font-weight: 660; letter-spacing: -.02em;
  color: var(--ink); text-decoration: none; flex: none;
}

/* A mark, so the corner is not four words in grey. Drawn rather than
   loaded: an image file here would be the first request to leave the page. */
.mark::before {
  content: ""; width: 1.375rem; height: 1.375rem; flex: none;
  border-radius: 7px; background: var(--accent);
  box-shadow: inset 0 -6px 0 -2px color-mix(in srgb, #000 18%, transparent);
}

.topbar nav { display: flex; flex-direction: row; gap: var(--s1); min-width: 0; }

.topbar nav a {
  display: block; padding: var(--s2) var(--s3); border-radius: var(--r-sm);
  color: var(--muted); text-decoration: none; white-space: nowrap;
  font-size: var(--t-md); font-weight: 520;
  transition: background-color .12s ease, color .12s ease;
}

.topbar nav a:hover { background: var(--sunk); color: var(--ink); }
.topbar nav a.on { background: var(--accent-soft); color: var(--accent); font-weight: 600; }

/* At the far end, where an application puts "who am I". */
.bar-account { margin-inline-start: auto; display: flex; align-items: center; gap: var(--s3); min-width: 0; }

/* --------------------------------------------------------------------------
   Switching company

   Which company is being acted for rides on every request, so it is named at
   the foot of the sidebar and changed from there. A menu rather than a page:
   the question has as many lines as the account has companies, and answering
   it should not cost the screen somebody was working on.
   -------------------------------------------------------------------------- */

.company-switch { position: relative; }

/* Scoped under .company-switch and .company-menu throughout, because these are buttons and the
   primary button rules carry :hover:not(:disabled) - two classes and an element, which outranks
   a bare class. Unscoped, the chip turned solid indigo under the cursor. */
.company-switch .company-chip {
  /* One line in a bar: the label would make it three deep and the bar tall enough to look
     like a second header. What it says is the name; the number settles which business that is. */
  display: flex; flex-direction: row; align-items: baseline; gap: var(--s2);
  /* Capped, because the ellipsis on the name below has nothing to work against otherwise: it
     only cuts what does not fit, and without a ceiling everything fits. A company called by
     sixty characters was making this button half the bar. */
  max-width: 22rem;
  padding: var(--s2) var(--s3); border: 1px solid transparent; border-radius: var(--r-sm);
  background: var(--sunk); text-align: start; cursor: pointer;
  font: inherit; color: inherit; box-shadow: none;
  transition: background-color .12s ease, border-color .12s ease;
}

.company-switch .company-chip:hover:not(:disabled) {
  background: var(--accent-soft); border-color: var(--accent-line);
}

.company-switch .company-chip:focus-visible {
  outline: none; border-color: var(--accent); box-shadow: var(--ring);
}

/* Cut rather than wrapped or widened. A name somebody chose for themselves has no length
   limit worth enforcing at the point they type it, so it is the display that gives way. The
   nowrap is inherited from the button; the rest is what turns it into an ellipsis. */
.chip-value, .chip-nip {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  /* A flex item will not shrink below its content without this, so the name pushed the chip
     wider instead of being cut by the rule above it. */
  min-width: 0;
}

.chip-value { font-size: var(--t-md); font-weight: 580; color: var(--ink); }

/* Ten digits, and the thing that settles which business this is. Flex shrinks every child in
   proportion, so under the cap the number was being cut alongside the name - and a NIP with
   its tail missing identifies nothing. The name gives way instead; it is the part somebody
   chose and can recognise from its first few words. */
.chip-nip { font-size: var(--t-xs); color: var(--muted); flex: none; }

/* Covers the page so a click anywhere dismisses the menu. A div rather than a button: it is a
   click target with nothing to announce, and as a button it would sit in the tab order between
   the menu and everything else. */
.menu-shade { position: fixed; inset: 0; z-index: 30; }

.company-menu, .menu-card {
  position: absolute; z-index: 31;
  display: flex; flex-direction: column; gap: 1px;
  padding: var(--s1); border: 1px solid var(--line-strong); border-radius: var(--r-md);
  background: var(--surface); box-shadow: var(--shadow-2);
  width: max-content; max-width: min(20rem, 80vw);
}

/* A fixed width, unlike the row menu above, which hugs four short labels. This one lists
   company names, and a registered name runs to sixty characters - with width:max-content the
   menu grew to whatever the longest one was and hung across the page. The width is the menu's
   decision; the names that do not fit are cut with an ellipsis, which the chip already does. */
.company-menu {
  inset-inline: auto 0; top: calc(100% + var(--s2));
  width: 16rem; min-width: 0; max-width: 80vw;
}

.company-menu .company-menu-item {
  display: flex; flex-direction: column; align-items: stretch; gap: 1px; width: 100%;
  padding: var(--s2) var(--s3); border: 0; border-radius: var(--r-sm);
  background: transparent; text-align: start; cursor: pointer; font: inherit;
  color: inherit; box-shadow: none;
}

.company-menu .company-menu-item:hover:not(:disabled) { background: var(--sunk); }
.company-menu .company-menu-item.on { background: var(--accent-soft); }
.company-menu .company-menu-item.on .chip-value { color: var(--accent); }


/* The account entries share the menu with the companies: same question, one place. Separated by
   a rule because switching company and signing out are different kinds of act. */
.company-menu-foot {
  margin-top: var(--s1); padding-top: var(--s1);
  border-top: 1px solid var(--line);
  display: flex; flex-direction: column; gap: 1px;
}

.company-menu-foot a, .company-menu .company-menu-foot button {
  display: block; width: 100%; text-align: start;
  padding: var(--s2) var(--s3); border: 0; border-radius: var(--r-sm);
  background: transparent; color: var(--muted); box-shadow: none;
  font: inherit; font-size: var(--t-sm); text-decoration: none; cursor: pointer;
}

.company-menu-foot a:hover, .company-menu .company-menu-foot button:hover:not(:disabled) {
  background: var(--sunk); color: var(--ink);
}


.workspace { flex: 1 1 auto; min-width: 0; padding: var(--s6) var(--s5) var(--s7); }
.workspace > main { max-width: 72rem; margin: 0 auto; }

/* The same column as the workspace, down to which element carries the width, so a page that can
   be read either way is the same page in both. It was 64rem, which left the help article narrower
   for a visitor than for a customer: the same complaint as centring it, one size smaller. Putting
   the cap on the outer box instead subtracts its own padding and misses by that much - the trap
   the top bar hit when it would not line up with the page under it. */
.shell { padding: var(--s6) var(--s5) var(--s7); }
.shell > main { max-width: 72rem; margin: 0 auto; }

/* The sign-in box, asked for by the screens that are one rather than inferred from there being
   no session. A help article read by somebody signed out is a page, not a box. */
.entry { display: flex; justify-content: center; }
.entry .card { width: 100%; max-width: 28rem; padding: var(--s6); }

/* A screen that is only a box is the whole screen: centred both ways, with the name over the box
   at title size. Left at the top of a full-height page the card read as a fragment of something
   larger that had failed to load, which is not the impression the first screen should give.

   Keyed on the box being there rather than on there being no session, so the article next to it
   is untouched. Signed in the same box lands in the workspace, where .entry alone centres it. */
.shell:has(.entry) {
  display: flex; align-items: center; justify-content: center;
  min-height: 100vh; padding: var(--s6) var(--s4);
}

/* Stretched, not centred: centring the children makes each of them shrink to its own contents,
   so the box came out narrower than the column and the fields narrower than the box. Only the
   name needs centring, and it can ask for that itself. */
.shell:has(.entry) > main { width: 100%; max-width: 28rem; gap: var(--s6); }
.shell:has(.entry) .mark { align-self: center; font-size: var(--t-xl); }
.shell:has(.entry) .mark::before { width: 1.75rem; height: 1.75rem; border-radius: 9px; }

main { display: flex; flex-direction: column; gap: var(--s5); align-items: stretch; }
main form { display: flex; flex-direction: column; gap: var(--s5); }

/* --------------------------------------------------------------------------
   Type
   -------------------------------------------------------------------------- */

h1 {
  font-size: var(--t-2xl); line-height: 1.2; letter-spacing: -.02em;
  font-weight: 640; margin: 0; text-wrap: balance;
}

/* Every navigation puts the focus on this heading, so a screen reader announces the page
   somebody has arrived at instead of leaving them where they were while the screen changed
   underneath. Focusing a heading means giving it tabindex="-1", and the browser then draws
   its focus ring around it - which on the sign-in page reads as the title being selected.
   The ring goes, the focus stays: the heading is out of the tab order, so the outline was
   marking something nobody can reach or act on. */
h1:focus { outline: none; }

h2 { font-size: var(--t-lg); line-height: 1.35; letter-spacing: -.01em; font-weight: 620; margin: 0; }
p { margin: 0; }

.muted { color: var(--muted); font-size: var(--t-md); }
.faint, .none { color: var(--faint); }
.none { font-style: italic; }
.small { font-size: var(--t-sm); }

.mono {
  font-family: ui-monospace, "Cascadia Code", "SF Mono", Consolas, monospace;
  font-size: .875em; letter-spacing: -.01em;
}

/* Links in running text keep their underline: inside a sentence it is what tells a reader there is
   a link at all. Everywhere else the underline is what makes an interface look like a document from
   1998, so links that are really controls get their own treatment below. */
a { color: var(--accent); text-underline-offset: 2px; text-decoration-thickness: 1px; }
a:hover { color: var(--accent-hover); }

/* A link that is a control rather than a word in a sentence. Underlined only when pointed at, and
   it lifts on hover the way anything clickable should. */
a.quiet, .swap a {
  text-decoration: none; font-weight: 550;
}

a.quiet:hover, .swap a:hover { text-decoration: underline; }

/* A badge that happens to link somewhere is still a badge. It was arriving underlined, inside the
   pill, which reads as a mistake rather than as an invitation. */
a.badge { text-decoration: none; }
a.badge:hover { border-color: currentColor; }

/* --------------------------------------------------------------------------
   Surfaces
   -------------------------------------------------------------------------- */

.card {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg);
  box-shadow: var(--shadow-2);
  padding: var(--s6); width: 100%; max-width: 24rem;
  display: flex; flex-direction: column; gap: var(--s4);
}

.card form { display: flex; flex-direction: column; gap: var(--s4); }

.panel {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg);
  box-shadow: var(--shadow-1);
  padding: var(--s5);
  display: flex; flex-direction: column; gap: var(--s4);
}

.panel h2 {
  font-size: var(--t-xs); text-transform: uppercase; letter-spacing: .06em;
  color: var(--faint); font-weight: 620;
}

/* A second group inside one panel needs a line to start from, or its heading floats between two
   sets of fields belonging to neither. Only ever the second and later: the first is the panel's
   own title and already has the panel edge above it. */
.panel h2 ~ h2 { margin-top: var(--s3); padding-top: var(--s5); border-top: 1px solid var(--line); }

.panel ul, .panel ol { margin: 0; padding-left: 1.2rem; display: flex; flex-direction: column; gap: var(--s2); }
.panel ol.steps { gap: var(--s3); }
.panel.danger { border-color: var(--bad-line); }

/* Columns written out in the markup, after both of the automatic ways failed in opposite
   directions. CSS columns flow content between them, so a panel growing by one line - a wrong
   password, a saved note - pushed every card after it into the other column and the page
   rearranged itself in front of somebody who had only made a typo. A grid holds each panel in
   its cell, but lines its rows up, so a one-line panel beside a form left a hole the height of
   the form.

   Two containers have neither problem: a panel cannot leave the column it is written in, and
   each column stacks on its own, so nothing waits for the other side. What it costs is that the
   split is a decision rather than an outcome - which is the right way round for five panels that
   are not interchangeable.

   Wrapping gives one column on a phone and two on a laptop, with the floor set where a labelled
   field stops being comfortable. */
.tiles { display: flex; flex-wrap: wrap; gap: var(--s5); align-items: start; }
.tile-column { flex: 1 1 24rem; min-width: 0; display: flex; flex-direction: column; gap: var(--s5); }

/* --------------------------------------------------------------------------
   Forms
   -------------------------------------------------------------------------- */

.field { display: flex; flex-direction: column; gap: var(--s1); min-width: 0; }
label { font-size: var(--t-md); font-weight: 550; color: var(--ink); }

/* textarea belongs in every one of these. Left out, it kept the browser default -
   monospace, at its own size - so the one multi-line field on the invoice form looked like a
   code editor dropped into the page. Nothing else on the site uses a fixed-width face. */
input, select, textarea {
  width: 100%; padding: .5625rem .75rem; font: inherit; font-size: var(--t-md);
  color: var(--ink); background: var(--surface);
  border: 1px solid var(--line-strong); border-radius: var(--r-sm);
  box-shadow: var(--shadow-1);
  transition: border-color .12s ease, box-shadow .12s ease;
}


/* The browser draws its own arrow hard against the right edge, close enough to read as a
   rendering slip rather than a control. Replaced with one of ours so the gap is a decision:
   appearance:none takes the native widget away, the chevron comes back as a background image
   in the palette's own muted colour, and the extra padding keeps a long option from running
   underneath it. One rule, and every select in the application follows it. */
select {
  appearance: none;
  padding-right: 2.25rem;
  background-image: var(--select-arrow);
  background-repeat: no-repeat;
  background-position: right var(--s3) center;
  background-size: 12px 8px;
}

input::placeholder, textarea::placeholder { color: var(--faint); }
input:hover:not(:disabled), select:hover:not(:disabled) { border-color: var(--muted); }

input:focus, select:focus, textarea:focus,
input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: none; border-color: var(--accent); box-shadow: var(--ring);
}

input:disabled, select:disabled, textarea:disabled { background-color: var(--sunk); color: var(--muted); box-shadow: none; }

.hint { font-size: var(--t-sm); color: var(--faint); }
.hint.found { color: var(--ok); }

label.checkbox, .note label { display: flex; align-items: center; gap: var(--s2); font-weight: 500; }
/* Selector without the label as well, because not every one of these sits in one - the paid
   marker on a received invoice does not. Sized up from the browser's 13px, which is a target
   from a decade of smaller screens and is genuinely hard to hit; flex:none so it keeps that
   size in the rows where it sits beside text that can shrink. */
input[type=checkbox] {
  width: 1.125rem; height: 1.125rem; flex: none;
  margin: 0; box-shadow: none; accent-color: var(--accent); cursor: pointer;
}

.row { display: flex; gap: var(--s4); flex-wrap: wrap; }
.row .field { flex: 1 1 12rem; }

/* A name, an address or an account number wants more of the row than the thing beside it.
   .grow was defined for .line only, so it had been sitting inertly on fields inside .row -
   including the account number on the invoice form - doing nothing at all. */
.row .grow { flex: 2 1 18rem; }

.line { display: flex; gap: var(--s3); align-items: flex-end; flex-wrap: wrap; }
.line .grow { flex: 1 1 14rem; }
.line .narrow { flex: 0 0 5rem; }
.line .short { flex: 0 0 8rem; }

/* Wider than .short, because the treatments this holds are named rather than numbered and a
   select that reads "nie podlega, …" for two different ones is worse than no label at all:
   they report to different boxes of the VAT return. */
.line .rate { flex: 0 0 12rem; }

/* A buyer takes more than one line whenever it is not a plain NIP, so a row needs a boundary
   of its own. Without one, where a wrapped buyer ends and the next one begins is a guess, and
   the button that removes a buyer sits against whichever row happens to be above it. */
.buyers .line { padding-block: var(--s3); border-top: 1px solid var(--line); }
.buyers .line:first-of-type { padding-top: 0; border-top: 0; }


/* --------------------------------------------------------------------------
   Choosing a country

   A list of two hundred and fifty is too long for a select and too long to
   remember, so the box is typed into and the list narrows underneath it. It is
   absolutely positioned so that opening it does not push the rest of the form
   down, which on a wrapped row would move the fields out from under the cursor.
   -------------------------------------------------------------------------- */

.picker-box { position: relative; }

.picker-list {
  position: absolute; z-index: 20; inset-inline-start: 0; top: calc(100% + var(--s1));
  margin: 0; padding: var(--s1); list-style: none;
  max-height: 15rem; overflow-y: auto;

  /* Sized by the names, not by the box that opened it. In a row of buyers that box is eight
     rems wide, and a list constrained to it clipped the country code off every entry and put a
     horizontal scrollbar under them. The code is half of what the list is for. */
  min-width: 100%; width: max-content; max-width: min(22rem, 80vw);
  background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: var(--r-sm); box-shadow: var(--shadow-2);
}

.picker-list li {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--s3);
  padding: var(--s2) var(--s3); border-radius: var(--r-sm);
  font-size: var(--t-md); cursor: pointer;
}

.picker-list li.on { background: var(--accent-soft); }

/* The code is shown next to the name because the code is what ends up on the
   invoice, and someone checking a document against this list is checking that. */
.picker-code { color: var(--faint); font-size: var(--t-sm); letter-spacing: .04em; }

/* --------------------------------------------------------------------------
   Choosing a file

   The browser's own control cannot be styled and looks nothing like the rest of
   the form. The input is taken out of the flow rather than hidden with
   display:none, which would take it out of the tab order with it; the label
   beside it is what gets drawn and what a click lands on.
   -------------------------------------------------------------------------- */

.file-field { display: flex; align-items: center; gap: var(--s3); }

.file-input {
  position: absolute; width: 1px; height: 1px;
  opacity: 0; overflow: hidden; white-space: nowrap;
}

.file-button {
  display: inline-flex; align-items: center; cursor: pointer;
  padding: .5rem .9rem; border: 1px solid var(--line-strong); border-radius: var(--r-sm);
  background: var(--sunk); color: var(--ink); font-size: var(--t-md); font-weight: 560;
}

.file-button:hover { background: var(--surface); border-color: var(--faint); }
.file-input:focus-visible + .file-button { outline: none; box-shadow: var(--ring); }
.file-name { color: var(--muted); font-size: var(--t-md); min-width: 0; overflow-wrap: anywhere; }

/* --------------------------------------------------------------------------
   Which company a screen is acting for

   Named on the screen rather than left to the chip in the sidebar: uploading a
   certificate or granting access to invoices happens to one company, and the
   wrong one is not a mistake anybody notices afterwards.
   -------------------------------------------------------------------------- */

.acting-for {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: var(--s4); flex-wrap: wrap;
  padding: var(--s4); margin-bottom: var(--s5);
  background: var(--sunk); border: 1px solid var(--line); border-radius: var(--r-md);
}

.acting-label { display: block; font-size: var(--t-xs); color: var(--muted); }
.acting-name { display: block; font-size: var(--t-lg); line-height: 1.3; }
.acting-nip { display: block; font-size: var(--t-sm); color: var(--muted); }
.acting-for .field { flex: 0 1 16rem; }

/* Naming a company for yourself, in place on its card. */
.rename { display: flex; align-items: center; gap: var(--s3); margin-top: var(--s2); flex-wrap: wrap; }
.rename input { flex: 1 1 12rem; }

/* --------------------------------------------------------------------------
   Buttons
   -------------------------------------------------------------------------- */

button, a.button {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s2);
  padding: .5625rem 1rem; font: inherit; font-size: var(--t-md); font-weight: 570;
  color: var(--accent-ink); background: var(--accent);
  border: 1px solid transparent; border-radius: var(--r-sm);
  box-shadow: var(--shadow-1);
  cursor: pointer; text-decoration: none; white-space: nowrap;
  transition: background-color .12s ease, border-color .12s ease, color .12s ease;
}

button:hover:not(:disabled), a.button:hover { background: var(--accent-hover); color: var(--accent-ink); }
button:focus-visible, a.button:focus-visible { outline: none; box-shadow: var(--ring); }
button:disabled { opacity: .5; cursor: default; }

button.ghost, a.button.ghost, .more, .remove {
  background: var(--surface); color: var(--ink); border-color: var(--line-strong);
}

button.ghost:hover:not(:disabled), a.button.ghost:hover, .more:hover:not(:disabled) {
  background: var(--sunk); color: var(--ink);
}

.remove { padding: .5rem .625rem; font-size: var(--t-lg); line-height: 1; color: var(--muted); box-shadow: none; }
.remove:hover:not(:disabled) { color: var(--bad); border-color: var(--bad-line); background: var(--bad-soft); }

/* These are actions - "switch to this company", "clear the filters", "send it again" - written as
   links because they belong beside text rather than in a row of buttons. Underlining them made
   every screen a page of blue underlined words. A tertiary control instead: no rule under it, a
   surface when pointed at, and enough padding to be worth aiming at on a phone. */
button.link {
  background: none; color: var(--accent);
  padding: .1875rem .375rem; margin: -.1875rem -.375rem;
  border: 0; border-radius: var(--r-sm); box-shadow: none;
  font-weight: 550; text-decoration: none;
}

button.link:hover:not(:disabled) { background: var(--accent-soft); color: var(--accent-hover); }
button.link:disabled { background: none; }
button.destructive, a.button.destructive { background: var(--bad); color: #fff; }
button.destructive:hover:not(:disabled) { background: var(--bad); opacity: .88; color: #fff; }

.card > form > button[type="submit"], button.issue { width: 100%; }
button.issue { padding: .75rem; font-size: var(--t-base); }

.actions { display: flex; gap: var(--s3); flex-wrap: wrap; align-items: center; }
.swap { font-size: var(--t-md); color: var(--muted); display: flex; gap: var(--s2); flex-wrap: wrap; }

/* --------------------------------------------------------------------------
   Actions that will not fit on a row

   Three of them on a contact wrapped onto two lines and put "Usuń" directly
   under "Wystaw fakturę" - one click from the thing somebody came to do to the
   one they cannot undo. Behind a button they are a list, and a list has room.
   -------------------------------------------------------------------------- */

/* Left as a real table cell, and centred with text-align rather than with flex.

   ".actions" is a flex row, which is right for a strip of buttons in a panel and wrong here:
   display:flex takes a td out of the table, the table wraps it in an anonymous cell, and the
   flex box then keeps its own height instead of filling the row. On a row one line tall that
   is invisible - which is exactly why measuring one said it was centred - but on a row whose
   neighbour wraps onto three lines the button stayed at the top, sixteen pixels above
   everything beside it. A table cell already centres its contents vertically. */
td.actions { display: table-cell; text-align: center; }

.row-menu { position: relative; display: inline-block; }

.row-menu-trigger {
  width: 2rem; height: 2rem; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--line-strong); border-radius: var(--r-sm);
  background: var(--surface); color: var(--muted); box-shadow: none;
  cursor: pointer;
  /* The dots are drawn, not typed. An ellipsis character sits on the baseline, so centring the
     line box leaves the ink low in the button, and no amount of align-items reaches it. Three
     round dots from one pseudo-element are centred exactly and take their colour from the
     button, which a background image could not.

     The button must stay empty for this to hold: with the character still in it, at font-size 0,
     the text went on taking part in the flex layout and the drawn dots came out five pixels left
     of centre. See RowMenu.razor. */
  line-height: 0;
}

.row-menu-trigger::before {
  content: ""; width: 3px; height: 3px; border-radius: 50%;
  background: currentColor;
  box-shadow: -6px 0 0 currentColor, 6px 0 0 currentColor;
}

/* Scoped past the primary-button rules, which carry :hover:not(:disabled) and outrank a bare
   class. Unscoped, every one of these turned solid indigo under the cursor. */
.row-menu .row-menu-trigger:hover:not(:disabled) {
  background: var(--sunk); color: var(--ink); border-color: var(--muted);
}

.row-menu .row-menu-trigger:focus-visible {
  outline: none; border-color: var(--accent); box-shadow: var(--ring);
}

/* Hung from the button at the end of the row, so it grows into the page rather than off it. */
.menu-card { inset-inline-end: 0; top: calc(100% + var(--s1)); min-width: 11rem; }

.menu-card a, .menu-card button {
  display: block; width: 100%; text-align: start; white-space: nowrap;
  padding: var(--s2) var(--s3); border: 0; border-radius: var(--r-sm);
  background: transparent; color: var(--ink); box-shadow: none;
  font: inherit; font-size: var(--t-md); text-decoration: none; cursor: pointer;
}

/* Colour and underline restated, not just the background. The menu is rendered inside a table
   cell, and "table a:hover" further down paints links with the accent colour and underlines
   them - so an item that happened to be a link lit up differently from the one below it, which
   happened to be a button. An item in this list is an item; what it is made of is not the
   reader's business. */
.menu-card a:hover, .menu-card button:hover:not(:disabled) {
  background: var(--sunk); color: var(--ink); text-decoration: none;
}

/* Same reasoning for the keyboard: a link would have fallen back to the browser's own outline
   while a button took the ring from the button rules. */
.menu-card a:focus-visible, .menu-card button:focus-visible {
  outline: none; background: var(--sunk); box-shadow: var(--ring);
}

/* The one that cannot be undone reads as itself, and sits last. */
.menu-card button.destructive { color: var(--bad); background: transparent; }
.menu-card button.destructive:hover:not(:disabled) { background: var(--bad-soft); color: var(--bad); }

/* --------------------------------------------------------------------------
   Which half of the register

   Issued and received share one entry in the navigation and split here. Two
   tabs rather than a select: there are two of them, both are always available,
   and a select would hide the one you are not on behind a click.
   -------------------------------------------------------------------------- */

.scope { display: flex; gap: var(--s1); border-bottom: 1px solid var(--line); }

.scope-tab {
  padding: var(--s2) var(--s3); margin-bottom: -1px;
  border-bottom: 2px solid transparent;
  color: var(--muted); text-decoration: none;
  font-size: var(--t-md); font-weight: 520;
  transition: color .12s ease, border-color .12s ease;
}

.scope-tab:hover { color: var(--ink); }
.scope-tab.on { color: var(--accent); border-bottom-color: var(--accent); font-weight: 620; }

/* --------------------------------------------------------------------------
   Notes
   -------------------------------------------------------------------------- */

.note {
  padding: var(--s3) var(--s4); border-radius: var(--r-md); font-size: var(--t-md);
  border: 1px solid transparent;
  display: flex; flex-direction: column; gap: var(--s2);
}

.note strong { font-weight: 620; }

/* A note that wraps its text in a div is one with a heading and a paragraph, and those need air
   between them. Named as a div so a span carrying one sentence is left as inline text. */
.note > div { display: flex; flex-direction: column; gap: var(--s1); }
.note.bad { background: var(--bad-soft); color: var(--bad); border-color: var(--bad-line); }
.note.info { background: var(--accent-soft); color: var(--ink); border-color: var(--accent-line); }
.note.warn { background: var(--wait-soft); color: var(--ink); border-color: var(--wait-line); }
.note.warn .sent { color: var(--ok); font-weight: 550; }

/* A note that offers a way out puts it beside the text rather than under it. Keyed on the control
   being there, because most notes are a sentence and nothing else, and those stay stacked. */
.note:has(> a), .note:has(> button), .note:has(> label) {
  flex-direction: row; flex-wrap: wrap; align-items: center; gap: var(--s4);
}

/* Only claim the width. Turning this into a column stack broke every note whose text is a plain
   sentence: a span's words became flex items and the sentence came apart onto three lines. Where
   the text is a div with a heading and a paragraph in it, being a block already stacks them. */
.note:has(> a) > :first-child,
.note:has(> button) > :first-child,
.note:has(> label) > :first-child {
  flex: 1 1 18rem;
}

/* The way out of whatever the note is about. Drawn in the note's own colour so it reads as part of
   the same statement, and shaped like a control because that is what it is. */
.note > a, .note > button.link {
  flex: none; padding: .375rem var(--s3); margin: 0;
  border: 1px solid currentColor; border-radius: var(--r-sm);
  color: inherit; background: none; text-decoration: none;
  font-size: var(--t-md); font-weight: 560;
}

.note > a:hover, .note > button.link:hover:not(:disabled) {
  background: color-mix(in srgb, currentColor 10%, transparent); color: inherit;
}

.note.info > a, .note.warn > a, .note.info > button.link, .note.warn > button.link { color: var(--accent); }

/* --------------------------------------------------------------------------
   Tables
   -------------------------------------------------------------------------- */

.table-scroll {
  overflow-x: auto; border: 1px solid var(--line); border-radius: var(--r-lg);
  background: var(--surface); box-shadow: var(--shadow-1);
}

/* A row menu opens out of the last cell, and this box clips it: overflow-x:auto forces the
   other axis to auto as well, so the card was cut off 44px short. The clipping is only there to
   let a wide table scroll, and nothing is being scrolled while somebody is reading a menu. */
.table-scroll:has(.menu-card) { overflow: visible; }

table { width: 100%; border-collapse: collapse; font-size: var(--t-md); }
th, td { text-align: left; padding: var(--s3) var(--s4); vertical-align: middle; }

th {
  font-size: var(--t-xs); font-weight: 620; color: var(--muted);
  text-transform: uppercase; letter-spacing: .04em;
  white-space: nowrap; background: var(--sunk); border-bottom: 1px solid var(--line);
}

tbody tr { border-top: 1px solid var(--line); }
tbody tr:first-child { border-top: 0; }
tbody tr:hover { background: var(--sunk); }

/* A KSeF number has no spaces to break at, so it wraps inside itself rather than
   setting the width of everything else. Amounts, dates and chips stay whole. */
td.mono { overflow-wrap: anywhere; color: var(--muted); }
td.right, td.when, td .badge { white-space: nowrap; }
th.right, td.right { text-align: right; }
td.when { color: var(--muted); }

tr.reason td { color: var(--bad); font-size: var(--t-sm); padding-top: 0; border: 0; }
tr.reason:hover { background: none; }
table a { color: var(--ink); font-weight: 550; text-decoration: none; }
table a:hover { color: var(--accent); text-decoration: underline; }
td .muted { font-weight: 400; }

.badge {
  display: inline-flex; align-items: center; gap: .375rem;
  padding: .125rem .5rem .125rem .4375rem; border-radius: var(--r-full);
  font-size: var(--t-xs); font-weight: 550; border: 1px solid transparent;
}

/* A dot as well as a colour, so the state survives being printed, photographed
   or read by somebody who does not separate red from green. */
.badge::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.badge.ok { background: var(--ok-soft); color: var(--ok); border-color: var(--ok-line); }
.badge.bad { background: var(--bad-soft); color: var(--bad); border-color: var(--bad-line); }
.badge.wait { background: var(--wait-soft); color: var(--wait); border-color: var(--wait-line); }
.badge.on { background: var(--accent-soft); color: var(--accent); border-color: var(--accent-line); }

/* --------------------------------------------------------------------------
   Page furniture
   -------------------------------------------------------------------------- */

/* min-height, so a head carrying a button is the same height as one carrying only a title.
   Two screens under one entry differing by the four pixels a button adds is exactly the kind of
   drift somebody notices without being able to name. */
.page-head { display: flex; align-items: center; gap: var(--s4); flex-wrap: wrap; min-height: 2.75rem; }
.page-head h1, .page-head h2 { margin-right: auto; }

.filters { display: flex; gap: var(--s4); flex-wrap: wrap; align-items: flex-end; }
.filters .field { flex: 1 1 14rem; }


.facts { display: grid; grid-template-columns: auto 1fr; gap: var(--s2) var(--s5); margin: 0; }
.facts dt { color: var(--muted); font-size: var(--t-md); }
.facts dd { margin: 0; }

/* The figure everybody scrolls to. Given its own surface and a size that says so,
   because a total in the same type as a field label is a total nobody finds. */
.totals { gap: var(--s2); background: var(--sunk); }
.totals div { display: flex; justify-content: space-between; gap: var(--s4); }
.totals span { color: var(--muted); }
.totals .gross {
  border-top: 1px solid var(--line-strong); padding-top: var(--s3); margin-top: var(--s1);
  font-size: var(--t-xl); font-weight: 660; letter-spacing: -.01em;
}

.totals .gross span { color: var(--ink); font-size: var(--t-base); font-weight: 560; align-self: center; }

/* Wraps at the hyphens, which are the number's own group boundaries, rather than wherever the
   line happens to run out: break-all left a single digit stranded on the second line of the one
   string on the screen somebody copies by hand. anywhere is the fallback for a box too narrow
   even for one group. */
.ksef { font-size: var(--t-lg); font-weight: 620; overflow-wrap: anywhere; }

/* Two of them on an offline invoice: the one that will confirm it reached KSeF, and the one
   that says who issued it while there is nothing there to confirm. Side by side while there is
   room, because the buyer needs both and they mean different things. */
.codes { display: flex; gap: var(--s5); flex-wrap: wrap; }
.codes > div { display: flex; flex-direction: column; gap: var(--s2); max-width: 22rem; }

.qr {
  width: 200px; height: 200px; image-rendering: pixelated;
  background: #fff; padding: 8px; border-radius: var(--r-md); border: 1px solid var(--line);
}

.preview { width: 100%; height: 70vh; border: 1px solid var(--line); border-radius: var(--r-md); background: #fff; }

/* --------------------------------------------------------------------------
   A window over the page

   For the invoice preview, which sat under the form: somewhere to scroll to
   past everything already filled in, with no way back but scrolling again.
   Over the page it is a thing to look at and dismiss.
   -------------------------------------------------------------------------- */

.modal {
  position: fixed; inset: 0; z-index: 50;
  display: flex; align-items: center; justify-content: center;
  padding: var(--s5);
  background: rgb(16 24 40 / .5);
  /* It holds focus so Escape reaches it, and the ring that would draw around the whole screen
     says nothing worth saying. */
  outline: none;
}

/* Narrow, for a dialog that asks a question rather than showing a document. */
.modal-card.narrow { width: min(28rem, 100%); height: auto; }

.modal-card {
  display: flex; flex-direction: column; gap: var(--s4);
  width: min(64rem, 100%);
  /* Tall, because the thing being looked at is a page of A4 - taller than it is wide. Sized
     to the window rather than to its contents, which left an invoice in a letterbox with two
     thirds of the screen empty above and below it. */
  height: min(48rem, 100%); max-height: 100%;
  padding: var(--s5); border-radius: var(--r-lg);
  background: var(--bg); box-shadow: var(--shadow-2);
  overflow: auto;
}

/* Takes what the card has left after the heading, rather than a fixed slice of the viewport
   the card has already been sized against. */
.modal-card .preview { flex: 1 1 auto; height: auto; min-height: 0; }

/* Pages of a document, stacked and scrolled - the shape a reader expects, and the reason the
   preview is images rather than a PDF: a browser's PDF viewer brings a toolbar, a page counter
   and a dark surround, none of which are the invoice. */
.pages {
  flex: 1 1 auto; min-height: 0; overflow: auto;
  display: flex; flex-direction: column; align-items: center; gap: var(--s4);
  padding: var(--s4); border-radius: var(--r-md); background: var(--sunk);
}

.page {
  display: block; width: 100%; max-width: 45rem; height: auto;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: #fff; box-shadow: var(--shadow-1);
}

/* Issuing, looking first and starting over are one decision, so they sit on one line. The
   first grows; the other two are as wide as their words. */
.issue-row { margin-top: var(--s2); }
.issue-row .issue { flex: 1 1 14rem; width: auto; }

a.nip { color: var(--muted); text-decoration: none; }
a.nip:hover { color: var(--ink); }

/* The direction is stated, not inherited. These sit on elements that are also panels, and a panel
   is a column; leaving the direction out left "flex: 1 1 20rem" applying to the height instead of
   the width, so every company card was three hundred pixels of nothing between its address and its
   buttons. Row is what these were written for and now what they say. */
.company { display: flex; flex-direction: row; gap: var(--s5); flex-wrap: wrap; align-items: flex-start; }

/* The one being acted for. Marked rather than merely stated, because on a list of
   several the sentence saying so reads the same as every other line of small print,
   and which company an invoice goes out as is the thing this screen settles. */
.company.active { border-color: var(--accent); box-shadow: var(--ring), var(--shadow-1); }
.company-main { flex: 1 1 20rem; display: flex; flex-direction: column; gap: var(--s1); }

/* The column stretches its children, and a button centres its own text, so the one that
   renames a company sat in the middle of the card looking like a heading.

   Direct children only. The rename row nested inside this column is a row, and there
   "start" means the top rather than the left - so Zapisz and Anuluj were pinned to the top
   of a 47px input and sat eleven pixels above its middle. A rule written for one axis has
   no business reaching a box laid out on the other. */
.company-main > .link { align-self: start; }
.company-side { display: flex; flex-direction: column; gap: var(--s2); align-items: flex-end; }
/* Four ways into a company: its token, its certificate, its permissions, its details. They are
   navigation, so they get the shape of something you press rather than four blue words in a row. */
.company-links { display: flex; flex-direction: row; gap: var(--s2); flex-wrap: wrap; }

.company-links a {
  padding: .4375rem var(--s3); border: 1px solid var(--line-strong); border-radius: var(--r-sm);
  color: var(--ink); background: var(--surface); text-decoration: none;
  font-size: var(--t-md); font-weight: 550;
  transition: background-color .12s ease, border-color .12s ease, color .12s ease;
}

.company-links a:hover { background: var(--accent-soft); border-color: var(--accent-line); color: var(--accent); }

.attachment-block { display: flex; flex-direction: column; gap: var(--s3); }
.spaced { display: flex; flex-direction: column; gap: var(--s4); }

@media (max-width: 56rem) {
  /* Mobile-first from here down. The desktop layout squeezed into a phone is what this replaces:
     navigation across the top that took a quarter of the screen, and tables that scrolled
     sideways. The target user works from a phone, so this is the primary shape, not the fallback. */
  .app { flex-direction: column; }

  /* A slim bar: the name, and the one control that answers "which company, and me". Everything
     that is navigation moved to the bottom, where a thumb reaches. */
  .topbar .bar-inner {
    justify-content: space-between;
    gap: var(--s3); padding: var(--s2) var(--s4); min-height: 3.25rem;
  }

  .bar-account { margin: 0; width: auto; }
  .company-switch .company-chip { flex-direction: row; align-items: baseline; gap: var(--s2); width: auto; }
  .chip-value { max-width: 40vw; }
  .chip-nip { display: none; }

  /* Opens downward from the top bar, and hangs off the end it was opened from. */
  .company-menu { top: calc(100% + var(--s2)); bottom: auto; inset-inline: auto 0; }

  /* Four destinations, always visible, in reach. A menu behind a button would hide four words
     behind a tap and give nothing back; that pattern is for twenty entries, not for four. */
  .topbar nav {
    position: fixed; inset: auto 0 0 0; z-index: 40;
    flex-direction: row; gap: 0;
    background: var(--surface); border-top: 1px solid var(--line);
    padding-bottom: env(safe-area-inset-bottom);
  }

  .topbar nav a {
    flex: 1 1 0; min-width: 0;
    display: flex; align-items: center; justify-content: center;
    /* 44px is the smallest target a finger hits reliably. */
    min-height: 3.25rem; padding: var(--s2) var(--s1);
    border-radius: 0; font-size: var(--t-sm); text-align: center;
  }

  .topbar nav a.on { background: transparent; color: var(--accent); font-weight: 620; box-shadow: inset 0 2px 0 var(--accent); }

  /* Room for the bar, so the last row of a list is not underneath it. */
  .workspace { padding: var(--s5) var(--s4) calc(4.5rem + env(safe-area-inset-bottom)); }

  /* Every list becomes a stack of cards. A table of six columns cannot be read on a phone, and
     scrolling it sideways hides the columns that say what the row is worth. */
  .table-scroll { overflow: visible; border: 0; background: none; box-shadow: none; }
  .table-scroll table, .table-scroll tbody, .table-scroll tr, .table-scroll td { display: block; width: 100%; }
  .table-scroll thead { display: none; }

  .table-scroll tr {
    background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg);
    box-shadow: var(--shadow-1); padding: var(--s3) var(--s4); margin-bottom: var(--s3);
  }

  .table-scroll td {
    display: flex; align-items: baseline; justify-content: space-between; gap: var(--s3);
    padding: var(--s1) 0; border: 0; text-align: start; white-space: normal;
  }

  /* The header the row lost, put back on the line it belongs to. */
  .table-scroll td::before {
    content: attr(data-label);
    flex: 0 0 auto; color: var(--faint); font-size: var(--t-sm);
    text-transform: uppercase; letter-spacing: .04em;
  }

  /* The first cell is what the row is, so it reads as a title rather than a labelled field. */
  /* Block, not flex: the first cell often carries a second line under the name - an address,
     a KSeF number - and as a flex row those sat beside it instead of beneath. */
  .table-scroll td.headline { display: block; font-weight: 620; font-size: var(--t-base); }
  .table-scroll td.headline::before { content: none; }
  .table-scroll td.actions { justify-content: flex-end; padding-top: var(--s2); }
  .table-scroll td.actions::before { content: none; }
  .table-scroll td:empty { display: none; }
}


@media (max-width: 40rem) {
  :root { --s5: 1.25rem; --s6: 1.5rem; }
  .workspace { padding: var(--s4) var(--s3) var(--s6); }
  .shell { padding: var(--s4) var(--s3) var(--s6); }
  h1 { font-size: var(--t-xl); }
  .card { padding: var(--s5); max-width: none; }
}

@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; }
}

/* Blazor's own error banner. Left in place because it is the only thing that
   appears when the runtime itself fails. */
#blazor-error-ui {
  display: none;
  position: fixed; bottom: 0; left: 0; right: 0; z-index: 1000;
  padding: var(--s3) var(--s5);
  background: var(--bad); color: #fff;
  box-shadow: 0 -2px 12px rgba(0, 0, 0, .2);
}

#blazor-error-ui a, #blazor-error-ui .dismiss { color: #fff; cursor: pointer; margin-left: var(--s3); }
