/*
  ===========================================================================
  Satori OS · the design system.

  Satori is a meditation foundation. The people reading this screen are
  practitioners and the handful of staff who keep the place running, and most
  of them are on a phone, on mobile data, often standing up. So the work this
  file does is not decoration. It is deciding what a person's eye should reach
  first, and then getting everything else out of the way.

  Three rules run through the whole file.

  1. WEIGHT IS SPENT, NOT SPREAD. A card is a box because a box means "this is
     one thing, and you can act on it". When everything is a box, nothing is.
     Headings, notes, lists and empty states are therefore not boxes; each has
     its own treatment further down, and none of them borrows the card's.

  2. A SCALE, NOT A GUESS. Every size in this file is one of nine type steps
     and every gap is a multiple of four. Screens should ask for a card title
     rather than for eighteen pixels, which is what the custom properties in
     :root are for. tests/design-system.cjs holds both: no font-size off the
     scale, no gap off the grid, anywhere in the file.

  3. THE ACCESSIBILITY WORK SURVIVES EVERY REDESIGN. 44px targets, 16px
     fields, a focus ring on everything, one reduced-motion guard, AA contrast
     on every text colour. Those were measured against real devices and they
     are not a style choice. tests/responsive.cjs, tests/device-acceptance.cjs
     and tests/design-system.cjs each hold a different part of it.

  A NOTE ON LITERALS, because it looks like an inconsistency and is not.
  tests/device-acceptance.cjs resolves this stylesheet against the markup the
  modules emit — cascade, specificity, media queries and all — to answer
  "is this control 44px on the screen it is actually on". Its model does not
  expand custom properties. A padding it cannot read is a padding it models as
  zero, which would make it report controls as too small and miss text running
  off the edge. So padding, and any length that decides the size of a control
  or the position of something fixed, is written as a literal here. Margin,
  gap, font-size on text, radius and weight are not read that way and use the
  tokens. Every literal is still on the scale, and the test proves it.
  ===========================================================================
*/

/* ========================================================== 1 · the tokens */

:root{
  color-scheme:light;
  font-family:system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;
  color:#302a2d;
  background:#faf8f6;
  font-size:16px;

  /* The palette. Wine on warm off-white, and greys already darkened until
     every one of them clears 4.5:1 on all five surfaces text can land on.
     These stay literal wherever tests/responsive.cjs reads them back, because
     a var() there would make the contrast check silently skip the colour. */
  --wine:#5a1f2b;
  --line:#e7dfe2;        /* a hairline between things */
  --line-strong:#d8ccd0; /* a hairline that has to be noticed */
  --edge:#877d81;        /* the boundary of a control: 3.4:1, WCAG 1.4.11 */

  /*
    The type scale. Nine steps, roughly a 1.15–1.2 ratio, named for the job
    rather than the size. Three of them do almost all the work:

      --text-sm    metadata, help text, table cells — the second voice
      --text-base  body copy, and every form field
      --text-3xl   the name of the screen you are on

    A screen title, a section heading and a card title are four steps, one
    case change and two weights apart, which is enough to tell them apart
    without reading a word of them.
  */
  --text-xs:12px;
  --text-sm:14px;
  --text-base:16px;
  --text-md:18px;
  --text-lg:20px;
  --text-xl:24px;
  --text-2xl:28px;
  --text-3xl:32px;
  --text-4xl:40px;

  --leading-tight:1.15;   /* a heading: lines belong to one another */
  --leading-snug:1.35;    /* a title of two or three lines */
  --leading-body:1.65;    /* prose, on a phone, in one hand */

  --weight-normal:400;
  --weight-medium:500;
  --weight-semi:600;
  --weight-bold:700;

  --tracking-kicker:.1em;  /* uppercase needs air between letters */
  --tracking-tight:-.02em; /* large type needs less */

  /*
    The spacing rhythm. One four-pixel step, at eight sizes, named for what
    the distance means — because "24px" is a measurement and not a reason.

    Unrelated things must not sit as close together as related ones. That is
    the whole of it, and it is why a screen has a shape at all.
  */
  --space-1:4px;   /* inside a control */
  --space-2:8px;   /* a label and the field it names */
  --space-3:12px;  /* two things that are the same thing */
  --space-4:16px;  /* two things in one group */
  --space-5:24px;  /* one group and the next */
  --space-6:32px;  /* the head of a screen and its body */
  --space-7:48px;  /* one section of a screen and the next */
  --space-8:64px;  /* the one thing on an otherwise empty screen */

  /* Radii. Five steps and no more, because a page where every box has its
     own corner has no family resemblance between its boxes. */
  --radius-mark:2px;     /* the shapes on a status pill */
  --radius-sm:8px;       /* a notice, a calendar day */
  --radius-control:10px; /* every button and every field: one family */
  --radius-md:12px;      /* a panel */
  --radius-lg:16px;      /* a card, a dialog */
  --radius-pill:999px;
}

*{box-sizing:border-box}
body{margin:0}
button,input,select,textarea{font:inherit}
button,a{touch-action:manipulation}

/* ====================================================== 2 · the typography */

/*
  Three kinds of heading, and they must never be mistaken for one another.

    h1              the screen. 32px, tight, near-black. One per screen.
    h2.section      a division of the screen. 14px, uppercase, grey, over a
                    hairline. It is a label for what follows, not a title.
    h2 (in a card)  the name of one thing. 18px, semibold, dark.

  Size, case, colour and weight all differ between them, so the hierarchy
  survives a greyscale print and a glance from across a desk.
*/
h1{font-size:var(--text-3xl);line-height:var(--leading-tight);letter-spacing:var(--tracking-tight);font-weight:var(--weight-semi);color:#241e21;margin:0 0 var(--space-3)}
h2{font-size:var(--text-md);line-height:var(--leading-snug);font-weight:var(--weight-semi);color:#2a2327;margin:0 0 var(--space-3)}
h3{font-size:var(--text-base);line-height:var(--leading-snug);font-weight:var(--weight-semi);color:#2a2327;margin:0 0 var(--space-2)}

p{line-height:var(--leading-body);margin:0 0 var(--space-3)}
p:last-child{margin-bottom:0}

/* The masthead line above a screen title. Quiet on purpose: it says where you
   are, not what you are looking at. */
.eyebrow{text-transform:uppercase;font-size:var(--text-xs);letter-spacing:var(--tracking-kicker);font-weight:var(--weight-semi);color:#7d5a66;margin:0 0 var(--space-2)}

/* The second voice: everything that qualifies something else. One size down,
   one shade back, and never carrying the only copy of anything. */
small,.muted{color:#6f6367;font-size:var(--text-sm);line-height:var(--leading-body)}
.identity small{color:#6f6367}

/* The sentence under a screen title is not metadata — it is the first thing a
   person reads after the title, so it takes a size of its own. */
.heading .muted{font-size:var(--text-md);line-height:var(--leading-snug);color:#55494e;margin:0}

/* A division of a screen. The hairline is what actually separates one section
   from the next; the words only name it. */
h2.section,h3.section{font-size:var(--text-sm);text-transform:uppercase;letter-spacing:var(--tracking-kicker);font-weight:var(--weight-semi);color:#6f6367;margin:var(--space-7) 0 var(--space-4);padding-bottom:10px;border-bottom:1px solid var(--line)}
/* Anything else marked as a section gets the break without the label type.
   Kept because .section is a class a screen can put on a <div>. */
.section{margin-top:var(--space-5)}

/* A note that does not deserve a box: an aside, a caveat, a count. */
.quiet{font-size:var(--text-sm);line-height:var(--leading-body);color:#6f6367;margin:0 0 var(--space-4)}

/* One line of facts about the thing above it, separated by their own spacing
   rather than by punctuation somebody has to type. */
.meta{display:flex;flex-wrap:wrap;gap:4px 16px;font-size:var(--text-sm);line-height:var(--leading-body);color:#6f6367;margin:0 0 var(--space-3)}

.compact{font-size:var(--text-sm)}

a{color:var(--wine);text-decoration:none}
/* A link inside a sentence is underlined, because colour alone does not tell
   a colour-blind reader that a word is a link. A link that is a button is
   not a word in a sentence and keeps its own shape. */
p a,li a{text-decoration:underline;text-underline-offset:3px}
p a.button,li a.button{text-decoration:none}

/*
  Numbers are data, not prose.

  A price, a count, a reference and a date are read by comparing them with the
  one above, which only works if the digits line up. Tabular figures do that;
  the weight is what separates a figure from the sentence around it.
*/
strong,.num,.amount,.stat,.ac-table td,.code{font-variant-numeric:tabular-nums}
strong{overflow-wrap:anywhere;font-weight:var(--weight-semi)}
.amount{font-weight:var(--weight-semi);letter-spacing:var(--tracking-tight)}
.amount.lead{font-size:var(--text-xl);line-height:var(--leading-tight)}
.code{font-size:var(--text-xl);letter-spacing:1px}

/* ========================================================== 3 · the shell */

.shell{display:grid;grid-template-columns:235px minmax(0,1fr);min-height:100vh;min-height:100dvh}
/* Headings wrap as balanced lines rather than leaving one word alone on the
   last line. Ignored where unsupported, which costs nothing. */
h1,h2{text-wrap:balance}
/* Skip to content: a keyboard user's way past the navigation. A button that
   focuses main rather than an anchor, because every href="#x" in this
   application is a screen and the dead-link crawl would read "#content" as
   one. Hidden until it has focus; then the first thing on the screen. */
.skip{position:absolute;left:-9999px;top:var(--space-2);z-index:120;min-height:44px;padding:10px 16px;
  background:#fff;color:var(--wine);border:1px solid var(--wine);border-radius:var(--radius-control)}
.skip:focus-visible{left:var(--space-2);outline:2px solid var(--wine);outline-offset:2px}
/* A board still being read keeps its shape: three columns of blocks where
   the columns will be, so the page does not collapse to one sentence and
   then jump when the answer lands. Static on purpose — a pulse would be one
   more animated rule for a state that lasts a second. */
.skeleton{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(290px,1fr);gap:var(--space-3);
  overflow:hidden;margin:0 0 var(--space-5)}
.skeleton .column{min-height:220px}
.skeleton .bone{height:14px;border-radius:var(--radius-mark);background:var(--line);margin:0 0 var(--space-2)}
.skeleton .bone.wide{width:70%}.skeleton .bone.short{width:40%}
.skeleton .taskcard{min-height:72px}
aside{padding:32px 20px;border-right:1px solid var(--line);background:white}
.brand{display:flex;gap:12px;align-items:center;font-size:var(--text-xl);font-weight:var(--weight-bold);letter-spacing:var(--tracking-tight)}
.brand img{width:48px;height:48px;border-radius:var(--radius-md)}
.brand small{display:block;font-size:var(--text-xs);letter-spacing:2px;text-transform:uppercase;font-weight:var(--weight-medium);margin-top:var(--space-1)}

nav{display:grid;gap:var(--space-1);margin-top:var(--space-6)}
/* 14px of padding, not 12: at 12 the row resolves to 43px and a navigation
   item is a control like any other. */
nav a{padding:14px;border-radius:var(--radius-control);color:#65565d;font-size:var(--text-base)}
nav a.active{background:#f1e6e9;color:var(--wine);font-weight:var(--weight-semi)}

.identity{margin-top:var(--space-6);padding-top:20px;border-top:1px solid var(--line);display:grid;gap:var(--space-2)}

main{width:100%;max-width:1180px;padding:40px;margin:auto;align-self:start}

/* The head of a screen: masthead, title, sentence, and whatever the screen's
   one obvious action is. It gets more room beneath it than anything else on
   the page, because that space is what says the title is not a list item. */
.heading{display:flex;justify-content:space-between;align-items:flex-start;gap:var(--space-5);margin-bottom:var(--space-6)}
.heading>div{min-width:0}

/* ======================================================= 4 · the surfaces */

/*
  WHAT A CARD IS FOR.

  A card is one thing a person can act on: an offering they can join, a
  booking they can pay for, a record they can open. It is a box because a box
  says "this is a unit, and its buttons belong to it".

  What a card is NOT for, and what to use instead:

    a heading with a list under it   .panel — space, not a box
    an explanation or a warning      .notice
    a caveat in passing              .quiet
    an enumeration                   .row inside .list
    nothing yet                      .empty

  Everything being a card is the same as nothing being one.
*/
.card{background:white;border:1px solid var(--line);border-radius:var(--radius-lg);padding:22px;margin:0 0 var(--space-3);min-width:0}
.grid>.card{margin-bottom:0}

/* A card that is a link is an object you can pick up, so it answers a pointer.
   The border does the answering rather than a shadow: a shadow on everything
   is the fastest way to make a calm page look anxious. */
.link-card{display:block}
.link-card:hover{border-color:#b48492}

/* A group with a heading and no box. Most of what is a card today is really
   one of these: the heading already says where the group starts, and the box
   was only repeating it. */
.panel{margin:0 0 var(--space-5);min-width:0}

/* A figure card: a card whose content is one number. Written as
   `class="card tile"` — it is a card, and the wine rule along the top is the
   only thing it adds. .card:has(.stat) gives the same treatment to the
   screens that were writing the figure before the class existed. */
.tile,.card:has(.stat){border-top:3px solid var(--wine)}
.stat{font-size:clamp(24px,2.5vw,32px);font-weight:var(--weight-semi);line-height:var(--leading-tight);letter-spacing:var(--tracking-tight);display:block;margin-bottom:var(--space-1);overflow-wrap:anywhere}

/*
  A notice. Something the screen wants to say rather than something a person
  can act on: a condition, a consequence, a refusal.

  The left bar carries the tone and the words carry the meaning, in that
  order, because a bar is read before a sentence is. The tone is never the
  only thing saying what happened.
*/
.notice{border-left:3px solid #ab7b89;background:#f4edef;border-radius:var(--radius-sm);padding:14px 18px;margin:0 0 var(--space-4);font-size:var(--text-sm);line-height:var(--leading-body)}
.notice.good{border-left-color:#3f6b4a;background:#f2f8f3}
.notice.warn{border-left-color:#8a5a12;background:#fbf5ea}
.notice.bad{border-left-color:#a1331f;background:#fbeeeb}
.notice.wide{grid-column:1/-1}

/* A list. Rows, separated by hairlines, with nothing around them. Two rows in
   a list are related; two cards are two things. */
.list{margin:0 0 var(--space-5);min-width:0}
.row{padding:14px 0;border-bottom:1px solid var(--line);display:flex;justify-content:space-between;align-items:center;gap:var(--space-4)}
.row:last-child{border-bottom:0}
.row>div{min-width:0}
.row strong{display:block;line-height:var(--leading-snug)}
.row small{display:block;margin-top:var(--space-1)}
.row>.actions{margin-top:0}

/* A label and its value, side by side, with the value reading as data. */
.kv{display:flex;justify-content:space-between;align-items:baseline;gap:var(--space-4);padding:8px 0;font-size:var(--text-base)}
.kv dt,.kv .key{color:#6f6367;font-size:var(--text-sm)}
.kv dd,.kv .value{margin:0;font-weight:var(--weight-semi);font-variant-numeric:tabular-nums;text-align:right}

/*
  An empty state.

  "None yet." is a full stop where a person needed a door. An empty list is
  the most common first impression this app gives — a new participant, a new
  wing, a Tuesday morning — and it has three jobs: say what belongs here, say
  why there is none, and say what to do about it.

    <div class="empty">
      <p class="empty-title">No sittings yet</p>
      <p>Sittings you join appear here, with the time and the place.</p>
      <div class="actions"><a class="button" href="#explore">Explore</a></div>
    </div>

  A dashed edge rather than a solid one: the box is a space waiting to be
  filled, not a thing in its own right. It still reads as a single
  <p class="empty"> for the screens that have not been rewritten yet.
*/
.empty{color:#6f6367;font-size:var(--text-base);line-height:var(--leading-body);background:#faf8f6;border:1px dashed var(--line-strong);border-radius:var(--radius-md);padding:20px;margin:0}
.empty .empty-title{color:#2a2327;font-size:var(--text-base);font-weight:var(--weight-semi);margin:0 0 var(--space-2)}
.empty p{margin:0 0 var(--space-2)}
.empty p:last-child{margin-bottom:0}
.empty .actions{margin-top:var(--space-4)}

.stack{display:grid;gap:var(--space-3)}
.grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--space-4)}
.grid.three{grid-template-columns:repeat(3,minmax(0,1fr))}

/* ======================================================== 5 · the controls */

/*
  A button. The border is #877d81 rather than a hairline because a white
  button on a white card with a faint edge has no boundary at all, and WCAG
  1.4.11 asks for 3:1 on the boundary of a control. It is the same colour the
  form fields already use, so the controls read as one family.

  44px in both directions is Apple's minimum and WCAG 2.5.5's. Khairul taps
  the same control one-handed at a gate while looking at the person in front
  of him. A missed tap there is not cosmetic.
*/
button,.button{border:1px solid #877d81;background:#fff;color:var(--wine);border-radius:var(--radius-control);padding:12px 18px;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:44px;min-width:44px;font-weight:var(--weight-semi);line-height:var(--leading-tight);text-align:center}
button:hover,.button:hover{background:#f3e9ec}
button:disabled{opacity:.5;cursor:not-allowed}

/* One primary action per screen, and it is the one the screen is for. */
.primary{background:var(--wine);color:white;border-color:var(--wine)}
.primary:hover{background:#783447}

/* A third weight, under the secondary: something available but not being
   offered. No border until it is pointed at. */
button.ghost{background:transparent;border-color:transparent;color:#6b5c60;font-weight:var(--weight-medium)}
button.ghost:hover{border-color:var(--edge)}

button.danger{background:#8f2d1a;border-color:#8f2d1a;color:#fff}
button.danger:hover{background:#a3391f}

.actions{display:flex;flex-wrap:wrap;gap:var(--space-3);margin-top:var(--space-4)}

/* =========================================================== 6 · the forms */

/*
  A form is a sequence, and it should look like one:

    label        what this is          14px, semibold, dark
    field        where you write       16px, full width, a visible boundary
    .help        how to answer it      14px, grey, under the field
    .notice      why it was refused    a red bar, pointing at the field
    the footer   send it               after a hairline, so it reads as the end

  The 16px on the field is not a preference. Under 16px, iOS Safari zooms the
  whole page the moment a field takes focus, and filling a form then means
  pinching back out after every answer. It has to stay an explicit literal,
  because `font:inherit` above would otherwise pull it down to the label's.
*/
label{display:grid;gap:var(--space-2);font-size:var(--text-sm);font-weight:var(--weight-semi);color:#3a3236}
input,select,textarea{width:100%;min-width:0;border:1px solid #877d81;border-radius:var(--radius-control);padding:12px;background:white;color:inherit;min-height:48px;font-size:16px;font-weight:var(--weight-normal)}
textarea{min-height:96px;resize:vertical;line-height:var(--leading-body)}
input[type=checkbox]{width:20px;min-height:20px;flex:0 0 20px}

/* Help text is the answer to "what do you want here", and it belongs with the
   field rather than with the label, so it does not inherit the label's
   weight. */
.help{font-size:var(--text-sm);font-weight:var(--weight-normal);color:#6f6367;line-height:var(--leading-body);margin:0}

/* A refused field. The mark is the red edge, the explanation is the sentence
   underneath, and aria-invalid / aria-describedby (set in os-core.js) tie the
   two together for anybody who cannot see either. */
input[aria-invalid=true],select[aria-invalid=true],textarea[aria-invalid=true]{border-color:#a1331f;box-shadow:inset 0 0 0 1px #a1331f}
label .notice{border-left-color:#a1331f;background:#fbeeeb;color:#8f2d1a;font-weight:var(--weight-normal);margin:0;padding:10px 14px}

.formgrid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--space-5) var(--space-4)}
.wide{grid-column:1/-1}

/* A tick box is tapped by its whole row, so the row is the target and the box
   only has to be big enough to see. */
.check{display:flex;align-items:center}
label.check{display:flex;align-items:center;gap:var(--space-2);font-weight:var(--weight-normal);font-size:var(--text-base);margin:var(--space-1) 0}
label.check input[type=checkbox],label.check input[type=radio]{width:24px;height:24px;min-height:24px;flex:0 0 24px;margin:0}
.check,label.check{min-height:44px}

fieldset{border:1px solid var(--line);border-radius:var(--radius-control);padding:12px 14px;margin:0}
fieldset legend{padding:0 6px;font-size:var(--text-sm);font-weight:var(--weight-semi);color:#3a3236}

.allowance{display:flex;align-items:center;gap:var(--space-3)}
.allowance input[type=number]{max-width:132px}

/*
  Swallows implicit form submission so pressing Enter in a decision form's
  reason field cannot pick the first button, which is always the approving
  one. It must be focusable-by-form but never reachable or visible.
*/
.enter-guard{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;min-height:0;min-width:0}

details{border-top:1px solid var(--line);padding-top:var(--space-4);margin-top:var(--space-2)}
summary{cursor:pointer;color:var(--wine);font-weight:var(--weight-semi);padding:12px 0;min-height:44px}
details .formgrid{margin-top:var(--space-3)}

.checklist{display:grid;gap:var(--space-2)}
.checklist a{padding:14px;background:#f8f4f5;border-radius:var(--radius-control);min-height:44px;display:flex;align-items:center}

/* ========================================================== 7 · the states */

/*
  WAITING, APPROVED, REFUSED, DONE.

  Four answers a person needs before they have read a word, and colour cannot
  be the thing that tells them apart: roughly one man in twelve cannot use it,
  and none of it survives a photocopy or a bright doorway at noon.

  So each state carries a shape as well as a tint, drawn in the pill's own
  text colour and therefore always visible:

    waiting   a hollow ring    — open, still going round
    approved  a filled disc    — closed, settled
    refused   a filled diamond — a stop, and it looks like one
    done      a flat bar       — finished, nothing further

  The shapes are drawn with an empty ::before, so a screen reader announces
  the word and nothing else.
*/
.pill,.badge{display:inline-flex;align-items:center;gap:var(--space-2);padding:4px 12px;border-radius:var(--radius-pill);font-size:14px;font-weight:var(--weight-semi);line-height:var(--leading-snug);white-space:nowrap;border:1px solid transparent}
.pill::before,.badge::before{content:"";flex:0 0 auto;width:8px;height:8px;border-radius:var(--radius-pill);border:2px solid currentColor}

/* Done, and the neutral fallback: a flat bar. */
.pill{background:#f1ecef;color:#5c4d54;border-color:#ddd2d6}
.pill::before{width:10px;height:3px;border-radius:var(--radius-mark);border:0;background:currentColor}
.badge{background:#f1ecef;color:#67565e;margin:0 var(--space-1) var(--space-2) 0}
.badge::before{width:10px;height:3px;border-radius:var(--radius-mark);border:0;background:currentColor}

/* Waiting: a ring, left open. */
.pill.wait{background:#f7ecd8;color:#7a4e0c;border-color:#e6d2ad}
.pill.wait::before{width:8px;height:8px;border:2px solid currentColor;border-radius:var(--radius-pill);background:transparent}
.pending{background:#fbf0d9;color:#7b5c19;border-color:#e9d6ac}
.pending::before{width:8px;height:8px;border:2px solid currentColor;border-radius:var(--radius-pill);background:transparent}

/* Approved: a disc, filled in. */
.pill.ok{background:#e3eee5;color:#2f5c3a;border-color:#c3ddc9}
.pill.ok::before{width:8px;height:8px;border-radius:var(--radius-pill);border:0;background:currentColor}
.good{background:#e7f1eb;color:#326248;border-color:#c7ded1}
.good::before{width:8px;height:8px;border-radius:var(--radius-pill);border:0;background:currentColor}

/* Refused: a diamond. A square on its corner reads as a stop even at 8px,
   and it is the only shape here with a point on it. */
.pill.bad{background:#f8e2dd;color:#8f2d1a;border-color:#e8c3ba}
.pill.bad::before{width:8px;height:8px;border-radius:var(--radius-mark);border:0;background:currentColor;transform:rotate(45deg)}

/* ====================================================== 8 · the components */

/* --- the dialog --------------------------------------------------------- */

dialog{width:min(690px,calc(100vw - 24px));max-height:calc(100dvh - 32px);border:1px solid var(--line);border-radius:var(--radius-lg);padding:0;color:inherit;overflow:auto;background:#fff}
dialog::backdrop{background:#29202770}
.modal-head{position:sticky;top:0;display:flex;justify-content:space-between;align-items:center;gap:var(--space-3);background:white;border-bottom:1px solid var(--line);padding:16px 24px;z-index:1}
.modal-head h2{margin:0;font-size:var(--text-lg)}
.modal-head button{border:0;padding:4px 12px;font-size:var(--text-xl)}
.modal-body{padding:24px}
/* The end of a form. The hairline is what says "there is nothing after
   this" — without it, a submit button is just another control in a list. */
.modal-footer{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:var(--space-3);grid-column:1/-1;margin-top:var(--space-4);padding-top:16px;border-top:1px solid var(--line)}

/* --- the welcome and the personal screens -------------------------------- */

.welcome{max-width:620px;margin:var(--space-8) auto;text-align:center}
.welcome .brand{justify-content:center}
.welcome h1{font-size:var(--text-4xl);margin-top:var(--space-6)}
.welcome .actions{justify-content:center}
.personal{max-width:850px}
.personal .hero{background:#f5eeef;border:0;padding:32px}

/* --- boards, calendars and people ---------------------------------------- */

.board{display:grid;grid-template-columns:repeat(4,minmax(180px,1fr));gap:var(--space-3);overflow-x:auto}
.board .card{padding:16px}
.calendar{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:var(--space-1)}
.day{border:1px solid var(--line);border-radius:var(--radius-sm);padding:8px;min-height:92px;background:#fff;font-size:14px;overflow-wrap:anywhere}
.day button{font-size:14px;padding:6px;min-height:44px;width:100%;margin-top:var(--space-1)}
.person-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--space-3)}
.person-grid button{display:grid;text-align:left;justify-content:stretch;padding:16px;min-width:0;overflow-wrap:anywhere}
.person-grid small{font-weight:var(--weight-normal)}

/* --- a conversation ------------------------------------------------------ */

/* Satori's replies and the person's own messages need to be told apart at a
   glance: a thread where everything looks the same is read wrong more often
   than it is read slowly. */
.thread{display:flex;flex-direction:column;gap:var(--space-3);margin:var(--space-3) 0}
.msg{border-left:3px solid var(--line);padding:8px 12px;background:#faf8f6;border-radius:0 var(--radius-sm) var(--radius-sm) 0}
.msg.mine{border-left-color:var(--wine);background:#fdf6f7}
.msg .who{font-size:var(--text-sm);font-weight:var(--weight-semi);margin:0 0 var(--space-1)}
.msg p{margin:0}

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

/* Somebody standing at a door with a scanner reads this at arm's length, so
   the outcome is large and the colour arrives before the words do. */
.scan-panel input{font-size:18px;font-family:var(--mono,ui-monospace,monospace)}
.scan-result{border-left:6px solid var(--line)}
.scan-result h2{margin:0 0 var(--space-1);font-size:var(--text-2xl)}
.scan-result .scan-name{font-size:var(--text-xl);font-weight:var(--weight-semi);margin:0 0 var(--space-2)}
.scan-result.in{border-left-color:#3f6b4a;background:#f2f8f3}
.scan-result.out{border-left-color:#8a5a12;background:#fbf5ea}
.scan-result.bad{border-left-color:#a1331f;background:#fbeeeb}

/* --- the bottom navigation ----------------------------------------------- */

/*
  Off above 650px, where the sidebar carries the same links. Declared here, in
  front of the phone block that turns it on, so that block wins.

  The links were 38px tall. Both Apple's and WCAG's guidance put the minimum
  at 44px, and this is the control people tap most often, frequently
  one-handed and often while doing something else: a guard at a gate, someone
  at a till during service. A missed tap there is not a cosmetic problem.
  min-height rather than padding, so the target grows without pushing the
  labels apart on a narrow screen.
*/
.mobile-nav{display:none}
.mobile-nav a{min-height:44px;display:flex;align-items:center;justify-content:center;padding:8px 12px;font-size:14px;text-align:center;flex-shrink:0}

/* --- the owner preview banner -------------------------------------------- */

.inspection-banner{display:flex;flex-wrap:wrap;align-items:center;gap:var(--space-3);padding:14px 20px;background:#5a1f2b;color:white;font-size:var(--text-sm)}
.inspection-banner a{color:white;text-decoration:underline;margin-left:auto;min-height:44px;display:inline-flex;align-items:center}

/* --- accounts: ledgers, totals and touch-friendly controls ---------------- */

.ac-tabs{display:flex;flex-wrap:wrap;gap:var(--space-2);border-bottom:1px solid var(--line);padding-bottom:12px;margin-bottom:var(--space-5)}
.ac-tabs a{padding:10px 14px;border-radius:var(--radius-control);font-weight:var(--weight-semi);font-size:var(--text-base);min-height:44px;display:inline-flex;align-items:center}
.ac-tabs a[aria-current=page]{background:var(--wine);color:white}
.ac-primary{margin-bottom:var(--space-5)}
.ac-table{width:100%;border-collapse:collapse;font-size:14px}
.ac-table th,.ac-table td{text-align:left;padding:12px 8px;border-bottom:1px solid var(--line);vertical-align:top}
/* A column name is information, so it stays at the same 14px as the data it
   heads; weight, colour and the tinted band are what separate it. */
.ac-table th{font-weight:var(--weight-semi);color:#65565d;background:#faf8f6;letter-spacing:.02em}
.ac-table th:first-child,.ac-table td:first-child{padding-left:0}
.ac-table td button{padding:8px 12px;white-space:nowrap}
.ac-filter{background:white;border:1px solid var(--line);padding:18px;border-radius:var(--radius-md);margin:var(--space-5) 0}
.ac-item{padding:18px 0;border-bottom:1px solid var(--line)}
.ac-journal-row{display:grid;grid-template-columns:2fr 1fr 1fr;gap:var(--space-3);padding:12px 0;border-bottom:1px solid var(--line)}

/* Before and after in the activity log. Monospaced values, so a changed
   number lines up with the one it replaced. */
table.changes{font-size:14px}
table.changes td:nth-child(2),table.changes td:nth-child(3){font-family:var(--mono,ui-monospace,monospace);font-variant-numeric:tabular-nums}
table.changes td:nth-child(2){color:#6f6367}

/* ================================================= 9 · horizontal overflow */

/*
  The page body must never scroll sideways. Anything genuinely wider than a
  phone scrolls inside its own box instead. Each rule below fixes something
  that was measured failing in a browser at 375px, not something that might.
*/

/* activity.js wraps its before/after table in .tablewrap, which no rule ever
   matched, so the table pushed the page to 431px at a 375px viewport. */
.tablewrap,.ac-table-wrap{max-width:100%;overflow-x:auto}

/* A URL is one unbreakable token. manage.js prints a full joining link in a
   <code>, and it ran 95px past the right edge and took the page with it. */
code,kbd,samp{overflow-wrap:anywhere}
.joining-url{display:block;font-size:var(--text-sm);line-height:var(--leading-body);background:#f4f0f1;border-radius:var(--radius-sm);padding:10px 12px;margin:0}

/* A grid item's automatic minimum size is its content, so a track declared
   minmax(0,1fr) still blows out if the item in it has no floor of its own.
   That is how a payment dialog laid its fields out 453px wide inside a 335px
   box and put the submit button off the right edge. */
.formgrid>*,.grid>*,.stack>*,.board>*,.person-grid>*,.checklist>*,.day>*,.list>*{min-width:0}
.wide{min-width:0}

/* ======================================================== 10 · the phone */

@media(max-width:900px){
  main{padding:24px}
  .shell{grid-template-columns:190px minmax(0,1fr)}
  aside{padding:24px 12px}
  .grid.three{grid-template-columns:repeat(2,minmax(0,1fr))}
  .person-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
}

@media(max-width:650px){
  .shell{display:block}
  /* The sidebar only: a bare aside{} here also hid the task panel and the
     calendar's panel, so a tapped task opened nowhere on a phone (OS-088). */
  .shell>aside{display:none}
  main{padding:24px 16px 104px}
  .heading{align-items:flex-start;flex-direction:column;gap:var(--space-2)}
  h1{font-size:var(--text-2xl)}
  .heading .muted{font-size:var(--text-base)}
  h2.section,h3.section{margin-top:var(--space-6)}
  .grid,.grid.three{grid-template-columns:1fr}
  .card{padding:20px}
  .formgrid{grid-template-columns:1fr}
  .modal-body{padding:20px}
  .modal-head{padding:16px 20px}
  .welcome{margin:var(--space-6) auto}
  .welcome h1{font-size:var(--text-3xl)}
  .row{align-items:flex-start;flex-wrap:wrap}
  .row>div:first-child{flex:1 1 200px}
  .row button{padding:10px 12px}
  .calendar{grid-template-columns:repeat(7,minmax(104px,1fr));overflow-x:auto;padding-bottom:8px}
  .day{padding:6px;min-height:88px}
  .person-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
  .personal .hero{padding:24px}
  .ac-tabs a{padding:8px 12px;font-size:var(--text-sm)}
  .ac-journal-row{grid-template-columns:1fr 1fr}
  .ac-journal-row>label:first-child{grid-column:1/-1}
  .ac-table th,.ac-table td{padding:10px 8px}

  /* The bottom navigation arrives where the sidebar goes. It scrolls
     sideways within itself rather than crushing eight labels into 375px. */
  .mobile-nav{display:flex;position:fixed;bottom:0;left:0;right:0;background:#fff;border-top:1px solid var(--line);padding:8px 4px max(12px,env(safe-area-inset-bottom));z-index:10;margin:0;gap:0;overflow-x:auto;justify-content:flex-start}
}

/* After the phone block on purpose: the safe area at the top of a notched
   phone is a property of the device, not of the width. */
main{padding-top:max(32px,env(safe-area-inset-top))}

@media print{
  body{background:white}
  dialog[open]{display:block!important;position:static;margin:0;width:100%;max-width:none;max-height:none;border:0;overflow:visible}
  .modal-body{padding:0}
  .ac-table-wrap{overflow:visible}
}

/* =========================================================== 11 · focus */

/*
  :focus-visible with no selector in front of it covers every interactive
  element there is, including any added later. 3px so it is visible over a
  border, offset so it does not sit on top of one.
*/
:focus-visible{outline:3px solid #8d3f59;outline-offset:2px}
/* The one surface the ring cannot be read against is the wine banner, where
   it has to invert rather than darken. */
.inspection-banner a:focus-visible{outline-color:#fff}

/* ===================================== 12 · the shared UX primitives  */

/*
  OS-048 · toast, busy, confirm.

  Motion first, so the rules below can refer to it. Every one of these is
  turned off by the prefers-reduced-motion guard at the end of this file,
  which is universal and !important and therefore wins wherever it is written.
  That guard is not a formality: a vestibular disorder is not something the
  next person to touch this file will remember to check.

  Nothing here animates for decoration. Each one is a state change a person
  would otherwise have to infer: something arriving, something leaving,
  something still running.
*/
@keyframes satori-toast-in{from{opacity:0;transform:translateX(-50%) translateY(16px)}to{opacity:1;transform:translateX(-50%) translateY(0)}}
@keyframes satori-toast-out{from{opacity:1;transform:translateX(-50%) translateY(0)}to{opacity:0;transform:translateX(-50%) translateY(10px)}}
@keyframes satori-dialog-in{from{opacity:0;transform:translateY(14px) scale(.985)}to{opacity:1;transform:translateY(0) scale(1)}}
@keyframes satori-fade-in{from{opacity:0}to{opacity:1}}
@keyframes satori-busy-in{from{opacity:0;transform:translateY(-100%)}to{opacity:1;transform:translateY(0)}}
@keyframes satori-busy-run{0%{transform:translateX(-105%)}100%{transform:translateX(365%)}}
@keyframes satori-spin{from{transform:rotate(0)}to{transform:rotate(360deg)}}
@keyframes satori-row-out{from{opacity:1;max-height:240px}to{opacity:0;max-height:0;padding-top:0;padding-bottom:0;border-bottom-width:0}}

/*
  The toast. #toast is the one live region that outlives a re-render, so its
  position and z-index stay exactly where they were: it clears the fixed
  bottom navigation by design, and tests/device-acceptance.cjs measures that.

  The stripe is a second channel, never the only one. The sentence has to be
  true on its own; the stripe only makes it faster to read. Dark ground, light
  stripe, because a tinted background would cost the text its contrast.
*/
#toast{display:none;position:fixed;bottom:88px;left:50%;transform:translateX(-50%);background:#30242a;color:white;padding:14px 22px;border-radius:var(--radius-control);z-index:100;max-width:90vw;line-height:var(--leading-body)}
.toast{border-left:5px solid transparent}
#toast.toast{animation:satori-toast-in .18s ease-out}
#toast.toast-ok{border-left-color:#7fc79a}
#toast.toast-warn{border-left-color:#f0c46a}
#toast.toast-bad{border-left-color:#f0947f}
/* Leaving is a class rather than a removal, so the message is still readable
   while it goes. JS sets display:none once it is over. */
#toast.is-leaving{animation:satori-toast-out .2s ease-in forwards}

/*
  The busy indicator. releaseBusy() builds #release-busy on first use: a bar
  that moves, a spinner, and a sentence. Three cues rather than one, because
  on mobile data the only question being asked is whether anything is still
  alive, and a single static element answers it badly.

  pointer-events:none, so a strip appearing under a thumb mid-tap cannot
  swallow the tap.
*/
#release-busy{display:none;position:fixed;top:0;left:0;right:0;z-index:120;pointer-events:none}
#release-busy.is-busy{display:block;animation:satori-busy-in .18s ease-out}
.busy-bar{height:3px;background:#e7dfe2;overflow:hidden}
.busy-bar i{display:block;height:100%;width:38%;background:var(--wine);animation:satori-busy-run 1.15s ease-in-out infinite}
.busy-label{display:flex;align-items:center;gap:var(--space-3);margin:0 auto;width:fit-content;max-width:calc(100vw - 24px);background:#30242a;color:#fff;font-size:var(--text-sm);line-height:var(--leading-body);padding:8px 16px;border-radius:0 0 var(--radius-md) var(--radius-md)}
/* Any control waiting on the server. The buttons are disabled through
   release.loading; this is the visible half, and it says waiting rather than
   refused. */
.is-busy{cursor:progress}
#release-busy.is-busy{cursor:auto}
.spinner{display:inline-block;flex:0 0 16px;width:16px;height:16px;border:2px solid #ffffff5c;border-top-color:#fff;border-radius:50%;animation:satori-spin .8s linear infinite}

/*
  The confirmation. An admin is never refused a legitimate action, so the
  dialog's job is to state what the action reaches and then get out of the
  way. The list is the part that matters: "12 units use this type" is the
  sentence that makes the difference between a decision and a reflex.

  The confirming button is styled to the weight of what it does, and a
  destructive one sits second, after Cancel, so the pointer and the tab order
  both reach the safe answer first.
*/
.confirm p{margin:0 0 var(--space-4)}
.confirm h3{font-size:var(--text-sm);letter-spacing:var(--tracking-kicker);text-transform:uppercase;color:#6f6367;margin:var(--space-5) 0 var(--space-2);font-weight:var(--weight-semi)}
.confirm-list{margin:0;padding:0;list-style:none;border:1px solid var(--line);border-radius:var(--radius-control);background:#faf8f6}
.confirm-list li{padding:12px 16px;border-bottom:1px solid var(--line);line-height:var(--leading-body);overflow-wrap:anywhere}
.confirm-list li:last-child{border-bottom:0}
.confirm-destructive .confirm-list{border-color:#e0bcb3;background:#fbf3f1}

/*
  A dialog that appears with no transition at all reads on a phone as the page
  having been replaced. 160ms is under the threshold at which a person starts
  waiting for it, and long enough to say that something arrived on top rather
  than instead.

  .is-leaving on a row is for a list somebody has just removed something from:
  the row collapses where it was, so the eye knows which one went.
*/
dialog[open]{animation:satori-dialog-in .16s ease-out}
dialog::backdrop{animation:satori-fade-in .16s ease-out}
.row.is-leaving{animation:satori-row-out .24s ease-in forwards;overflow:hidden}

/* A press that shows nothing has to be taken on trust, and on a slow
   connection there is nothing else to go on for the next second. */
button,.button{transition:background-color .12s ease-out,border-color .12s ease-out,transform .06s ease-out}
button:active,.button:active{transform:scale(.985)}

/* ============================================= 13 · the motion guard */

/*
  Everything that animates in this file is declared above, and this guard is
  universal and !important, so it wins over all of it wherever it is written.
  Every rule added after this point was written on the assumption that this
  block exists.
*/
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}
}

/* ------------------------------------------------------------------ *
 * The task board (#tasks)
 *
 * Four ways of looking at the same tasks — board, list, timeline, and one
 * task opened in full. They share the card, the pill and the chip, so that
 * moving between them feels like turning a page rather than changing
 * application. Nothing here invents a colour: priority and stage both borrow
 * the pill tones already defined above.
 * ------------------------------------------------------------------ */

/* The view switcher. Underline rather than a button group, because these are
   four views of one thing, not four actions. */
.viewtabs{display:flex;gap:var(--space-1);flex-wrap:wrap;border-bottom:1px solid var(--line);margin:0 0 var(--space-4)}
.viewtab{background:transparent;border:0;border-bottom:2px solid transparent;border-radius:0;
  padding:10px 14px;min-height:44px;color:#6b5c60;font-size:var(--text-base);font-weight:var(--weight-medium)}
.viewtab:hover{color:#2a2327}
.viewtab.active{color:var(--wine);border-bottom-color:var(--wine);font-weight:var(--weight-semi)}

.groupby{display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap;
  color:#6f6367;font-size:var(--text-sm);margin:0 0 var(--space-3)}
.linkish{background:transparent;border:0;padding:6px 8px;min-height:44px;
  color:#6b5c60;font-size:var(--text-sm);text-decoration:underline;border-radius:var(--radius-sm)}
.linkish.active{color:var(--wine);font-weight:var(--weight-semi)}

/* Columns scroll sideways as a set. Each keeps a readable measure rather than
   dividing the screen by however many columns there happen to be. */
.kanban{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(290px,1fr);
  gap:var(--space-3);overflow-x:auto;padding-bottom:var(--space-3);margin:0 0 var(--space-5)}
.kanban .column{background:#f4f1ee;border:1px solid var(--line);border-radius:var(--radius-lg);
  padding:var(--space-3);min-width:0;display:flex;flex-direction:column;gap:var(--space-2)}
.column-head{display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap}
.column-head .muted{margin-left:auto}
.column-body{display:flex;flex-direction:column;gap:var(--space-2);min-width:0}

button.icon{min-width:44px;min-height:44px;padding:0;font-size:var(--text-lg);line-height:var(--leading-tight)}

/* One task, small enough that a column of them can be read at a glance. */
.taskcard{background:#fff;border:1px solid var(--line);border-radius:var(--radius-md);
  padding:14px;text-align:left;width:100%;min-width:0;display:block;cursor:pointer;overflow-wrap:anywhere}
.taskcard:hover{border-color:var(--line-strong)}
.taskcard:focus-visible{outline:2px solid var(--wine);outline-offset:2px}
.taskcard h3{font-size:var(--text-base);font-weight:var(--weight-semi);line-height:var(--leading-snug);margin:0 0 var(--space-1)}
.taskcard p{margin:0 0 var(--space-2)}
.taskcard footer{display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap;
  justify-content:space-between;font-size:var(--text-sm)}
.taskcard footer.who{margin-top:var(--space-1);justify-content:flex-start}
.clamp{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}

/* A type or a group: a label, not a state, so no dot and no colour. */
.chips{display:flex;gap:var(--space-1);flex-wrap:wrap;margin:0 0 var(--space-2)}
.chip{display:inline-block;padding:2px 10px;border-radius:var(--radius-pill);
  background:#f1ecef;color:#5c4d54;border:1px solid #ddd2d6;font-size:var(--text-xs);font-weight:var(--weight-semi)}
.chip.soft{background:transparent;color:#6f6367}

/* Initials, never a photograph. Satori holds no staff photographs and a
   generated face would be a person this system made up. */
.avatar{display:inline-flex;align-items:center;justify-content:center;width:26px;height:26px;
  border-radius:var(--radius-pill);background:#efe6e8;color:var(--wine);
  font-size:var(--text-xs);font-weight:var(--weight-semi);border:1px solid #e0d3d7}
.avatars{display:inline-flex;align-items:center;gap:4px;flex-wrap:wrap}

/* Stated progress. The number is printed next to the bar, because a bar on
   its own is a shape and the figure is the fact. */
.progress{display:flex;align-items:center;gap:var(--space-2);margin:0 0 var(--space-2);font-size:var(--text-sm)}
.progress .track{flex:1;height:6px;border-radius:var(--radius-pill);background:#eee7e9;overflow:hidden;min-width:60px}
.progress .fill{display:block;height:100%;background:var(--wine);border-radius:var(--radius-pill)}

/* A week of work. Days are columns on a desktop and a plain stack on a phone,
   where seven columns would be seven unreadable slivers. */
.weeknav{display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap;margin:0 0 var(--space-3)}
.timeline{display:grid;grid-template-columns:repeat(7,minmax(190px,1fr));gap:var(--space-2);
  overflow-x:auto;padding-bottom:var(--space-3);margin:0 0 var(--space-5)}
.tlday{border:1px solid var(--line);border-radius:var(--radius-md);background:#fff;
  padding:var(--space-2);min-width:0;display:flex;flex-direction:column;gap:var(--space-2)}
.tlday.istoday{border-color:var(--wine);box-shadow:inset 0 2px 0 var(--wine)}
.tlday>header{display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap;
  font-size:var(--text-sm);font-weight:var(--weight-semi)}
.column .empty,.tlday .empty{padding:10px;font-size:var(--text-sm);border-radius:var(--radius-sm)}
.cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:var(--space-2)}

/* The route a task takes. The step it has reached is filled; everything
   before it is ticked; what is left is outlined. */
.stepper{display:flex;flex-wrap:wrap;gap:var(--space-1);list-style:none;padding:0;margin:0 0 var(--space-4)}
.stepper li{font-size:var(--text-sm);padding:6px 12px;border-radius:var(--radius-pill);
  border:1px solid var(--line-strong);color:#6f6367;background:#fff}
.stepper li.done{background:#e3eee5;color:#2f5c3a;border-color:#c3ddc9}
.stepper li.done::before{content:"\2713\00a0"}
.stepper li.now{background:var(--wine);color:#fff;border-color:var(--wine);font-weight:var(--weight-semi)}

/* One task in full, above the board rather than instead of it. */
.taskpanel{background:#fff;border:1px solid var(--line-strong);border-radius:var(--radius-lg);
  padding:22px;margin:0 0 var(--space-5);min-width:0}
.taskpanel>header{display:flex;align-items:flex-start;gap:var(--space-3);justify-content:space-between}
.taskpanel>header h2{margin:0 0 var(--space-1)}
.taskpanel-body{margin:var(--space-3) 0 0}
.taskpanel-foot{margin-top:var(--space-4);border-top:1px solid var(--line);padding-top:var(--space-3)}
.facts{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:var(--space-3);margin:0 0 var(--space-4)}
.fact dt{font-size:var(--text-xs);text-transform:uppercase;letter-spacing:var(--tracking-kicker);
  color:#6f6367;font-weight:var(--weight-semi);margin:0 0 4px}
.fact dd{margin:0;font-size:var(--text-base)}
.feed{display:flex;flex-direction:column;gap:var(--space-2);margin:var(--space-3) 0 0}
.form-section{margin:var(--space-4) 0 0;font-size:var(--text-md);
  padding-top:var(--space-3);border-top:1px solid var(--line)}

@media (max-width:720px){
  .timeline{grid-template-columns:1fr;overflow-x:visible}
  .kanban{grid-auto-flow:row;grid-auto-columns:auto;overflow-x:visible}
  .facts{grid-template-columns:1fr}
}

/* The list view reuses .ac-table, so a task list reads like every other
   table in the system rather than like a second one. */
.tasks-filters{margin-bottom:var(--space-4)}
.taskgroup{margin:0 0 var(--space-5)}
.taskgroup-head{display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap;margin:0 0 var(--space-2)}
.taskgroup-head .muted{margin-right:auto}
.tasktable td.name strong{display:block}
.tasktable td.name small{display:block;font-size:var(--text-xs)}
.tasktable td.desc{max-width:280px;color:#6f6367}
.tasktable td.due{white-space:nowrap}
.tasktable tr.done td{opacity:.65}
.tasktable tr.editing td{background:#faf8f6}
.due-over{font-weight:var(--weight-semi);color:#8f2d1a}
.due-soon{font-weight:var(--weight-semi)}

/* The board's toolbar: the controls fold away, but an active filter is
   always named here, so nobody reads a filtered board as the whole truth. */
.toolbar{display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap;margin:0 0 var(--space-3)}

/* Late work, carried above the week it fell out of. */
.overdue-strip{border:1px solid #e8c3ba;background:#fdf6f4;border-radius:var(--radius-lg);
  padding:var(--space-3);margin:0 0 var(--space-3)}
.overdue-strip h3{display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap;
  font-size:var(--text-base);margin:0 0 var(--space-2)}

/* A permission and what it confers, at the point of granting. */
.scope-check{align-items:flex-start}
.scope-check span{display:block}
.scope-check small{display:block;line-height:var(--leading-snug);margin-top:var(--space-1)}
/* Said, not only coloured: a grant that will not appear anywhere. */
.warnline{background:#fdf6f4;border:1px solid #e8c3ba;border-left-width:3px;
  border-radius:var(--radius-sm);padding:10px 12px;margin:var(--space-2) 0;
  font-size:var(--text-sm);line-height:var(--leading-body)}

/* ------------------------------------------------------------------ *
 * The calendar (#calendar)
 *
 * A month grid whose cells carry counts rather than titles: the grid shows
 * where the weight falls, and the day underneath shows what it is made of.
 * ------------------------------------------------------------------ */
.monthgrid{display:grid;grid-template-columns:repeat(7,1fr);gap:var(--space-1);
  margin:0 0 var(--space-4)}
.monthhead{font-size:var(--text-xs);text-transform:uppercase;letter-spacing:var(--tracking-kicker);
  color:#6f6367;font-weight:var(--weight-semi);text-align:center;padding:var(--space-2) 0}
.monthcell{display:flex;flex-direction:column;align-items:flex-start;gap:var(--space-1);
  min-height:84px;padding:var(--space-2);background:#fff;border:1px solid var(--line);
  border-radius:var(--radius-sm);text-align:left;min-width:0;overflow:hidden}
.monthcell:hover{border-color:var(--line-strong)}
.monthcell .num{font-size:var(--text-sm);font-weight:var(--weight-semi)}
/* Days either side of the month are shown, so the weeks are whole, and muted,
   so nobody reads them as part of it. */
.monthcell.outside{background:#faf8f6;color:#8b7f84}
.monthcell.istoday{border-color:var(--wine);box-shadow:inset 0 2px 0 var(--wine)}
.monthcell.chosen{border-color:var(--wine);background:#fdf6f7}
.monthcell .mark{display:block;font-size:var(--text-xs);font-weight:var(--weight-semi);
  border-radius:var(--radius-mark);padding:2px var(--space-1);max-width:100%;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mark.run{background:#e3eee5;color:#2f5c3a}
.mark.due{background:#f7ecd8;color:#7a4e0c}
.mark.late{background:#f8e2dd;color:#8f2d1a}

ul.plain{list-style:none;padding:0;margin:0 0 var(--space-3)}
ul.plain li{padding:var(--space-2) 0;border-bottom:1px solid var(--line);
  display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap}
ul.plain li:last-child{border-bottom:0}

@media (max-width:720px){
  /* Seven columns survive on a phone because a cell is a number and a count,
     not a title. The day underneath is where the reading happens. */
  .monthcell{min-height:60px;padding:var(--space-1)}
}

/* A task on the personal queue: the board's card, plus the things a personal
   queue has that a board card has no room for. */
.taskline h3{font-size:var(--text-md);font-weight:var(--weight-semi);
  line-height:var(--leading-snug);margin:0 0 var(--space-1)}
.taskline>p:last-child{margin-bottom:0}
.taskmeta{display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap;
  margin:0 0 var(--space-2);font-size:var(--text-sm)}

/* ------------------------------------------------------------------ *
 * Menu icons
 *
 * Stroked in currentColor so an entry's mark is the same colour as its word
 * in every state, including the active one, without a second rule to keep in
 * step. Decorative: every entry keeps its word, and nothing is told by the
 * icon alone.
 * ------------------------------------------------------------------ */
.navicon{width:20px;height:20px;flex:0 0 auto;fill:none;stroke:currentColor;
  stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}
aside nav a{display:flex;align-items:center;gap:var(--space-2)}
/* On a phone the bar is five destinations across a small screen, so the mark
   sits above its word rather than beside it. */
@media (max-width:900px){
  .mobile-nav a{flex-direction:column;gap:var(--space-1);line-height:var(--leading-tight)}
  .mobile-nav .navicon{width:22px;height:22px}
}

/* ------------------------------------------------------------------ *
 * Reload, on every screen
 *
 * Top right, where a reload belongs and where nothing else is fixed: the
 * toast is bottom-centre, the navigation bar is bottom, and the progress line
 * is a hairline across the very top that this sits below. Quiet until it is
 * wanted — a hairline and a white face, the same as any control at rest.
 * ------------------------------------------------------------------ */
.refresh-float{position:fixed;right:var(--space-4);top:var(--space-4);
  z-index:50;width:44px;height:44px;min-height:44px;padding:0;
  display:flex;align-items:center;justify-content:center;
  background:#fff;color:var(--wine);border:1px solid var(--line-strong);
  border-radius:var(--radius-pill);box-shadow:0 1px 3px rgba(48,36,42,.08)}
.refresh-float:hover{border-color:var(--wine)}
.refresh-float[disabled]{opacity:.5}
.refresh-float .navicon{width:20px;height:20px}
/* Until the window is wide enough for main's own margin to hold it, the
   button is over the content — and the heading puts its actions in exactly
   that corner. So the heading keeps clear of it rather than running under it.
   Above that width main is centred and the button sits outside it. */
/* Always, not only below 1280px: the reload sits in the window's corner, and
   at any width a heading's own actions can reach that corner. */
.heading{padding-right:56px}

/* Dragging a card between columns. An enhancement: the same moves are buttons
   on the card once it is open, which is what works on a touchscreen. */
.taskcard[draggable="true"]{cursor:grab}
.taskcard.dragging{opacity:.5;cursor:grabbing}
.kanban .column.over{border-color:var(--wine);background:#fdf6f7}
.dragnote{font-size:var(--text-sm);margin:0 0 var(--space-2)}
/* Hidden at the width the navigation already calls a phone. (hover:none) is
   the truer test and tests/device-acceptance models width only, so this uses
   the breakpoint the rest of the stylesheet uses rather than introducing a
   query the device model cannot answer. */
@media (max-width:900px){
  .dragnote{display:none}
}

/* ------------------------------------------------------------------ *
 * The bar on a phone
 *
 * Four destinations with Create between them, each an equal share of the
 * width so the thumb has the same target wherever it lands, and Create in
 * the middle because that is where the thumb rests.
 * ------------------------------------------------------------------ */
@media (max-width:900px){
  .mobile-nav{justify-content:space-around;gap:0;overflow-x:visible}
  .mobile-nav a{flex:1 1 0;min-width:0;padding:var(--space-1) var(--space-1);
    font-size:var(--text-xs);text-align:center}
  /* Wraps rather than truncates. A label cut off with an ellipsis is a label
     somebody has to guess at, and tests/device-acceptance is right to refuse
     it — four entries have room for their words. */
  .mobile-nav a span{display:block;max-width:100%}
  .mobile-nav .active{color:var(--wine);font-weight:var(--weight-semi)}
  /* Raised out of the row, which is what makes it read as the action rather
     than the fifth place to go. */
  .barcreate{flex:0 0 auto;width:52px;height:52px;min-height:52px;padding:0;margin:0 var(--space-1);
    display:flex;align-items:center;justify-content:center;
    background:var(--wine);color:#fff;border:0;border-radius:var(--radius-pill);
    font-size:var(--text-2xl);line-height:var(--leading-tight);
    box-shadow:0 2px 6px rgba(90,31,43,.3);transform:translateY(-10px)}
  .barcreate:hover{background:#4a1823}
}
/* On a wide screen the bar is not shown at all, so neither is this. */
.barcreate{display:none}
@media (max-width:900px){ .barcreate{display:flex} }

/* Overview: the work counted by stage. Each count is a button, because each
   opens the board cut that way. */
.ovtiles{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:var(--space-3);margin:0 0 var(--space-5)}
.ovtile{display:flex;flex-direction:column;align-items:flex-start;gap:var(--space-1);text-align:left;
  min-height:96px;padding:var(--space-4);background:#fff;border:1px solid var(--line);border-radius:var(--radius-lg);color:inherit}
.ovtile:hover{border-color:var(--wine)}
.ovtile.bad{border-color:#e8c3ba;background:#fdf6f4}
.ovnum{font-size:var(--text-3xl);font-weight:var(--weight-semi);line-height:var(--leading-tight);font-variant-numeric:tabular-nums}
.ovlabel{font-size:var(--text-sm);color:#6f6367}

/* Calendar: titles on the day, and the filter. Titles are hidden on a phone,
   where a cell is a number and a count. */
.caltitles{display:flex;flex-direction:column;gap:var(--space-1);width:100%;min-width:0}
.calitem{display:block;font-size:var(--text-xs);line-height:var(--leading-snug);padding:2px var(--space-1);
  border-left:2px solid var(--wine);background:#fdf6f7;border-radius:var(--radius-mark);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.calitem.late{border-left-color:#8f2d1a;background:#fdf6f4}
.calitem.done{border-left-color:#2f5c3a;background:#f2f7f3;color:#5b6f60}
.calmore{font-size:var(--text-xs);color:#6f6367}
.monthcell{min-height:104px}
.calfilter{display:flex;flex-wrap:wrap;align-items:flex-end;gap:var(--space-3);margin:0 0 var(--space-3)}
.calfilter label{min-width:160px}
.tasklink{padding:0;text-decoration:none;color:inherit;min-height:44px;text-align:left}
.tasklink:hover strong{text-decoration:underline}
@media (max-width:720px){ .caltitles{display:none} .monthcell{min-height:60px} }

/* Overview charts: quiet, one hue family, every number also in words. */
.ovcharts{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:var(--space-3);margin:0 0 var(--space-5)}
.ovchart{background:#fff;border:1px solid var(--line);border-radius:var(--radius-lg);padding:var(--space-4)}
.ovchart h3{font-size:var(--text-sm);font-weight:var(--weight-semi);margin:0 0 var(--space-3);color:#55494e}
.stackbar{display:flex;height:10px;border-radius:var(--radius-pill);overflow:hidden;background:#eee7e9}
.stackbar span{display:block;height:100%}
.legend{display:flex;flex-wrap:wrap;gap:var(--space-1) var(--space-3);margin:var(--space-2) 0 0;font-size:var(--text-xs);color:#6f6367}
.legend i{display:inline-block;width:8px;height:8px;border-radius:2px;margin-right:var(--space-1);vertical-align:middle}
.c-todo{background:#d9c7cc}.c-doing{background:#a3707c}.c-review{background:#5a1f2b}
.c-low{background:#e5dde0}.c-medium{background:#c7aab2}.c-high{background:#8f4a59}.c-urgent{background:#5a1f2b}
.duespark{display:block;width:100%;height:56px}
.duespark .b-on{fill:#8f4a59}.duespark .b-off{fill:#e5dde0}
.loadbar{display:block;width:96px;height:6px;margin-top:var(--space-2);border-radius:var(--radius-pill);background:#eee7e9;overflow:hidden}
.loadbar span{display:block;height:100%;background:#8f4a59}

/* ------------------------------------------------------------------ *
 * The task form (OS-067)
 *
 * One centred card: the essentials in two columns, then a single fold whose
 * contents sit in a tinted panel. Narrow enough to read down, never the
 * width of the window.
 * ------------------------------------------------------------------ */
.taskform-page{max-width:760px;margin:0 auto}
.taskform{background:#fff;border:1px solid var(--line);border-radius:var(--radius-lg);padding:var(--space-5);margin:0 0 var(--space-5)}
.taskform-grid{display:grid;grid-template-columns:1fr 1fr;gap:var(--space-4);align-items:start}
.taskform-grid .wide{grid-column:1/-1}
.taskform-grid label{display:flex;flex-direction:column;gap:var(--space-1);min-width:0}
.taskform-grid label>span{font-size:var(--text-sm);font-weight:var(--weight-semi)}
.taskform-grid input,.taskform-grid select,.taskform-grid textarea{width:100%}
.req{color:var(--wine);font-weight:var(--weight-semi)}
.hint{font-size:var(--text-xs);color:#6f6367;line-height:var(--leading-snug)}
.moreopts{margin:var(--space-4) 0 0;border-top:1px solid var(--line);padding-top:var(--space-3)}
.moreopts>summary{display:flex;align-items:baseline;gap:var(--space-3);flex-wrap:wrap}
.moreopts>summary small{font-weight:var(--weight-normal);color:#6f6367;font-size:var(--text-sm)}
.taskform-grid.panel{background:#faf8f6;border:1px solid var(--line);border-radius:var(--radius-md);padding:var(--space-4);margin-top:var(--space-2)}
.contribs{border:0;padding:0;margin:0;display:flex;flex-wrap:wrap;gap:var(--space-2)}
.contribs legend{font-size:var(--text-sm);font-weight:var(--weight-semi);margin:0 0 var(--space-1);padding:0}
.chipcheck{flex-direction:row!important;align-items:center;gap:var(--space-2)!important;min-height:44px;
  padding:0 var(--space-3);border:1px solid var(--line-strong);border-radius:var(--radius-pill);background:#fff;cursor:pointer}
/* The ticked chip is marked through its text, not :has() — a selector the
   device suite cannot model, which would make its checks optimistic. */
.chipcheck input:checked+span{color:var(--wine);font-weight:var(--weight-semi)}
.chipcheck span{font-size:var(--text-sm);font-weight:var(--weight-medium)}
.taskform-foot{display:flex;gap:var(--space-2);align-items:center;margin-top:var(--space-4);padding-top:var(--space-4);border-top:1px solid var(--line)}
@media (max-width:640px){
  .taskform{padding:var(--space-4)}
  .taskform-grid{grid-template-columns:1fr}
}

/* ------------------------------------------------------------------ *
 * Calendar layout (OS-069): the month, and the chosen day beside it.
 * ------------------------------------------------------------------ */
.callayout{display:grid;grid-template-columns:minmax(0,1fr) 340px;gap:var(--space-4);align-items:start}
.calpanel{position:sticky;top:var(--space-4);background:#fff;border:1px solid var(--line);border-radius:var(--radius-lg);padding:var(--space-5)}
.calpanel h2{font-size:var(--text-xl);margin:0 0 var(--space-1)}
.calpanel h3{font-size:var(--text-sm);font-weight:var(--weight-semi);margin:var(--space-4) 0 var(--space-2)}
.calpanel .otheractions{border-top:1px solid var(--line);padding-top:var(--space-4)}
.paneltasks,.callist{list-style:none;padding:0;margin:var(--space-3) 0}
.paneltasks li{margin:0 0 var(--space-2)}
.paneltask,.calrow{display:block;width:100%;text-align:left;background:#fff;border:1px solid var(--line);
  border-radius:var(--radius-md);padding:var(--space-3);min-height:44px;color:inherit}
.paneltask:hover,.calrow:hover{border-color:var(--wine)}
.calline{display:flex;align-items:flex-start;gap:var(--space-2);width:100%}
.caltext{display:flex;flex-direction:column;flex:1;min-width:0}
.caltext .muted{font-size:var(--text-sm)}
.wide-btn{width:100%;justify-content:center}
.calviews{display:inline-flex;margin-left:auto;border:1px solid var(--line-strong);border-left:0;border-radius:var(--radius-control);overflow:hidden}
/* Every button carries its own left border, and the group has none, so
   the divider needs no sibling selector the device suite cannot model. */
.calview{border:0;border-left:1px solid var(--line-strong);border-radius:0;background:#fff;color:#55494e;min-height:40px;padding:0 var(--space-4)}
.calview.active{background:var(--wine);color:#fff;font-weight:var(--weight-semi)}
.callist li{margin:0 0 var(--space-2)}
.caldate{display:block;font-size:var(--text-xs);color:#6f6367;margin:0 0 var(--space-1)}
.weekgrid .monthcell{min-height:240px}
/* A dot per task, by priority, so the month shows where the weight is. */
.dot{display:inline-block;flex:0 0 auto;width:8px;height:8px;border-radius:var(--radius-pill);margin:var(--space-2) var(--space-1) 0 0}
.p-urgent{background:#c0392b}.p-high{background:#e07a5f}.p-medium{background:#d9a441}.p-low{background:#4f8a5b}.p-done{background:#a39a9e}
.calitem{display:flex;align-items:center;border-left:0;background:transparent;padding:2px 0}
.calitem .dot{margin:0 var(--space-1) 0 0}
.calitem.late{background:transparent;color:#8f2d1a}
.calitem.done{background:transparent}
.monthcell .mark.phoneonly{display:none}
@media (max-width:980px){
  .callayout{grid-template-columns:1fr}
  .calpanel{position:static}
}
@media (max-width:720px){ .monthcell .mark.phoneonly{display:block} }

/* ------------------------------------------------------------------ *
 * The right-hand panel, across the platform (OS-070)
 *
 * One layout for any screen that opens one thing out of many: the many stay
 * where they are, and the one opens beside them. The calendar's day panel is
 * the same shape; this is it, named once, for every screen.
 * ------------------------------------------------------------------ */
.withpanel{display:grid;grid-template-columns:minmax(0,1fr) 380px;gap:var(--space-4);align-items:start}
.sidepanel{position:sticky;top:var(--space-4);background:#fff;border:1px solid var(--line);border-radius:var(--radius-lg);
  padding:var(--space-5);max-height:calc(100dvh - 32px);overflow:auto}
.sidepanel .taskpanel{border:0;padding:0;margin:0;background:transparent}
.sidepanel .card{box-shadow:none}
.sidepanel-close{float:right;min-width:44px;min-height:44px}
@media (max-width:980px){
  .withpanel{grid-template-columns:1fr}
  .sidepanel{position:static;max-height:none;order:-1}
}

/* ---------- OS-071: the board and its panel, as in the owner's sample ---------- */
.boardbar{display:flex;align-items:flex-end;gap:var(--space-3);flex-wrap:wrap;margin:0 0 var(--space-4);border-bottom:1px solid var(--line)}
.boardbar .viewtabs{border-bottom:0;margin:0;flex:1 1 auto}
.boardsearch{display:flex;align-items:center;gap:var(--space-2);margin:0 0 var(--space-2);padding:0 var(--space-3);
  background:#fff;border:1px solid var(--line-strong);border-radius:var(--radius-control);min-height:44px;flex:0 1 240px}
.boardsearch input{border:0;outline:0;background:transparent;min-height:44px;padding:0;width:100%;font-size:var(--text-base)}
.boardsearch .navicon,.filterbtn .navicon,.prow .navicon{width:18px;height:18px;flex:none;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;color:var(--muted,#6f6569)}
.filterbtn{display:inline-flex;align-items:center;gap:var(--space-2);margin:0 0 var(--space-2);background:#fff;border:1px solid var(--line-strong)}
.filternote{flex:1 1 100%;margin:0 0 var(--space-2);font-size:var(--text-sm)}
.visually-hidden{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}

.kanban .column{background:#f4f1ee;border:0;padding:var(--space-3)}
.column-head{flex-wrap:nowrap;margin:0 0 var(--space-3);padding:0 var(--space-1)}
.column-head h2{font-size:var(--text-base);font-weight:var(--weight-semi);margin:0;line-height:var(--leading-snug)}
.column-head .count{margin-left:auto;font-weight:var(--weight-semi);font-variant-numeric:tabular-nums}
.stagedot{width:12px;height:12px;border-radius:50%;border:2px solid #d9a441;flex:none}
.stagedot.doing{border-color:#3f8f5a}
.stagedot.done,.stagedot.cancelled,.stagedot.ok,.stagedot.bad{border-color:#8a8086}
.taskcard{border:0;box-shadow:0 1px 2px rgba(60,30,40,.08);padding:var(--space-4)}
.taskcard.current{outline:2px solid var(--wine);outline-offset:0}
.taskcard h3{display:flex;gap:var(--space-2);align-items:flex-start}
.taskcard .cardsub{font-size:var(--text-sm);margin:0 0 var(--space-3)}
.taskcard footer{justify-content:space-between;margin:0}
.taskcard.isdone{opacity:.65}
.taskcard.isdone h3{text-decoration:line-through;color:#8a8086}
.taskcard .tick{display:inline-grid;place-items:center;width:20px;height:20px;border-radius:var(--radius-mark);background:#8a8086;color:#fff;font-size:var(--text-xs);flex:none}
.prio{display:inline-flex;align-items:center;gap:var(--space-2);font-size:var(--text-sm);font-weight:var(--weight-medium);
  padding:4px 12px;border-radius:var(--radius-pill);background:#fbeee4;color:#8a5a12}
.prio::before{content:"";width:8px;height:8px;border-radius:50%;background:currentColor}
.prio.urgent{background:#fde8e8;color:#b3261e}
.prio.high{background:#fdeee4;color:#b4531a}
.prio.low{background:#e8f0fb;color:#2d5fa8}
.addtask{width:100%;background:transparent;border:1px solid var(--line);color:#5f565a;font-weight:var(--weight-medium)}
.addtask:hover{background:#fff}

/* the panel */
.withpanel{grid-template-columns:minmax(0,1fr) 400px}
.sidepanel{padding:var(--space-5,20px);max-height:calc(100dvh - 32px);overflow-y:auto}
.taskpanel>header{align-items:flex-start;gap:var(--space-1)}
.taskpanel>header>div{flex:1 1 auto;min-width:0}
.taskpanel>header h2{font-size:var(--text-lg);line-height:var(--leading-snug)}
.moremenu{position:relative}
.moremenu>summary{list-style:none;cursor:pointer;min-width:44px;min-height:44px;display:grid;place-items:center;border-radius:var(--radius-control)}
.moremenu>summary::-webkit-details-marker{display:none}
.moremenu-list{position:absolute;right:0;top:48px;z-index:20;background:#fff;border:1px solid var(--line-strong);border-radius:var(--radius-md);
  padding:var(--space-2);display:flex;flex-direction:column;gap:var(--space-1);min-width:160px;box-shadow:0 8px 24px rgba(60,30,40,.12)}
.moremenu-list button{width:100%;justify-content:flex-start;text-align:left}
.stagepills{display:flex;flex-wrap:wrap;gap:var(--space-2);margin:0 0 var(--space-4)}
.stagepill{font-size:var(--text-xs);font-weight:var(--weight-medium);padding:4px 12px;min-height:32px;display:inline-flex;align-items:center;
  border-radius:var(--radius-pill);border:1px solid var(--line-strong);background:#fff;color:#4a4246}
.stagepill.past{background:#e3eee5;border-color:#c3ddc9;color:#2f5c3a}
.stagepill.now{background:#fbeccb;border-color:#e9cf8c;color:#7a5410}
button.stagepill:hover{border-color:var(--wine)}
.prows{display:flex;flex-direction:column;gap:var(--space-2);margin:0 0 var(--space-4)}
.prow{display:grid;grid-template-columns:20px 96px minmax(0,1fr);align-items:center;gap:var(--space-3);min-height:44px}
.plabel{font-size:var(--text-sm);color:#4a4246}
.pvalue{min-width:0;font-size:var(--text-sm)}
.pvalue select{width:100%;min-height:44px;font-size:var(--text-base)}
.ptitle{font-size:var(--text-sm);font-weight:var(--weight-semi);margin:0 0 var(--space-2)}
.pdetail{width:100%;font-size:var(--text-base);line-height:var(--leading-body)}
.pstamp{font-size:var(--text-xs);margin:var(--space-3) 0 0;line-height:var(--leading-body)}
@media (max-width:1100px){.withpanel{grid-template-columns:1fr}}

/* ---------- OS-072: Overview, as in the owner's sample ---------- */
.ovstats{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--space-4);margin:0 0 var(--space-4)}
.ovstat{display:flex;align-items:center;gap:var(--space-4);text-align:left;background:#fff;border:1px solid var(--line);
  border-radius:var(--radius-lg);padding:var(--space-5,20px);min-height:96px;color:inherit;font-weight:var(--weight-normal)}
.ovstat:hover{border-color:var(--line-strong)}
.ovstat.hot{background:#fdf1f2;border-color:#f0cfd4}
.ovicon{width:56px;height:56px;border-radius:50%;display:grid;place-items:center;flex:none;background:#fbf2dc;color:#c7961f}
.ovicon svg{width:28px;height:28px;fill:none;stroke:currentColor;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round}
.ovstat.in_progress .ovicon{background:#e3f1e7;color:#2f8a4b}
.ovstat.in_progress .ovicon svg{fill:currentColor}
.ovstat.submitted .ovicon{background:#fbe3e6;color:#b3261e}
.ovstat.late .ovicon{background:#efebec;color:#6f6569}
.ovnum{display:block;font-size:var(--text-xl);font-weight:var(--weight-bold);line-height:var(--leading-tight);font-variant-numeric:tabular-nums}
.ovlabel{display:block;font-size:var(--text-sm);color:#4a4246}
.ovgrid{display:grid;grid-template-columns:1.2fr 1fr 1.2fr;gap:var(--space-4);margin:0 0 var(--space-6,24px)}
.ovcard{margin:0}
.ovcard h2{font-size:var(--text-base);margin:0 0 var(--space-4)}
.ovcardhead{display:flex;align-items:center;justify-content:space-between;gap:var(--space-2);margin:0 0 var(--space-3)}
.ovcardhead h2{margin:0}
.ovcardhead select{width:auto;font-size:var(--text-base);min-height:44px}
.ovring{display:flex;align-items:center;gap:var(--space-5,20px);flex-wrap:wrap}
.ring{width:160px;height:160px;border-radius:50%;display:grid;place-items:center;flex:none}
.ring>div{width:104px;height:104px;border-radius:50%;background:#fff;display:grid;place-content:center;text-align:center}
.ring strong{font-size:var(--text-lg);line-height:var(--leading-tight)}
.ring span{font-size:var(--text-sm)}
.ovlegend,.ovbars{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--space-3);flex:1 1 160px;font-size:var(--text-sm)}
.ovlegend li{display:grid;grid-template-columns:12px 1fr auto;gap:var(--space-2);align-items:center}
.ovbars li{display:grid;grid-template-columns:12px 64px 1fr 24px;gap:var(--space-3);align-items:center}
.ovbars strong{text-align:right}
.dot{width:12px;height:12px;border-radius:50%;display:inline-block}
.track{height:12px;border-radius:var(--radius-pill);background:#efeaeb;overflow:hidden}
.track>span{display:block;height:100%;border-radius:var(--radius-pill)}
.ovdue{display:grid;grid-template-columns:24px 1fr auto;gap:var(--space-3);width:100%;text-align:left;align-items:start;
  background:#fff;border:1px solid var(--line);border-radius:var(--radius-md);padding:var(--space-3) var(--space-4);margin:0 0 var(--space-2);color:inherit;font-weight:var(--weight-normal)}
.ovdue-main{display:flex;flex-direction:column;align-items:flex-start;gap:var(--space-1);min-width:0;font-size:var(--text-sm)}
.ovdue-when{display:flex;flex-direction:column;align-items:flex-end;font-size:var(--text-sm)}
.ovdue-when.due-over strong{color:#b3261e}
.prio.only{padding:0;background:transparent;width:24px;height:24px;justify-content:center}
.stagechip{display:inline-flex;align-items:center;gap:var(--space-2);font-size:var(--text-xs);font-weight:var(--weight-medium);
  padding:4px 12px;border-radius:var(--radius-pill);background:#fbeccb;color:#7a5410}
.stagechip::before{content:"";width:8px;height:8px;border-radius:50%;border:2px solid currentColor}
.ovsection{display:flex;align-items:center;justify-content:space-between;margin:var(--space-4) 0 var(--space-3)}
.ovsection h2{font-size:var(--text-lg);margin:0}
.ovtable{background:#fff;border:1px solid var(--line);border-radius:var(--radius-lg);margin:0 0 var(--space-4)}
.ovperson{display:inline-flex;align-items:center;gap:var(--space-3)}
.clickrow{cursor:pointer}
@media (max-width:1100px){.ovstats{grid-template-columns:repeat(2,minmax(0,1fr))}.ovgrid{grid-template-columns:1fr}}
@media (max-width:560px){.ovstats{grid-template-columns:1fr}}
.cardlock{font-size:var(--text-xs);margin-left:auto;margin-right:var(--space-2)}
.kanban{grid-auto-columns:minmax(208px,1fr)}
.taskpanel>header{align-items:center}
.moremenu>summary{font-size:var(--text-lg);line-height:var(--leading-tight);padding:0;margin:0;border:0;background:transparent}
.moremenu>summary:hover{background:#f4f1ee}

/* ---------- OS-075: one people picker, with the search inside ---------- */
.combo{position:relative;display:flex;flex-direction:column;gap:var(--space-2);min-width:0}
.combo-label{font-weight:var(--weight-semi);font-size:var(--text-sm)}
.combo-btn{display:flex;align-items:center;justify-content:space-between;width:100%;min-height:44px;text-align:left;
  background:#fff;border:1px solid var(--edge);border-radius:var(--radius-control);padding:0 var(--space-4);
  font-size:var(--text-base);font-weight:var(--weight-normal);color:inherit}
.combo-btn::after{content:"";width:8px;height:8px;border-right:2px solid currentColor;border-bottom:2px solid currentColor;transform:rotate(45deg);margin:0 0 var(--space-1) var(--space-3);flex:none}
.combo-pop{position:absolute;left:0;right:0;top:100%;z-index:30;margin-top:var(--space-1);background:#fff;
  border:1px solid var(--line-strong);border-radius:var(--radius-md);box-shadow:0 8px 24px rgba(60,30,40,.14);padding:var(--space-2)}
.combo-search{width:100%;min-height:44px;font-size:var(--text-base);margin:0 0 var(--space-2)}
.combo-list{list-style:none;margin:0;padding:0;max-height:248px;overflow-y:auto}
.combo-list li{padding:var(--space-3);border-radius:var(--radius-sm);cursor:pointer;font-size:var(--text-base);line-height:var(--leading-snug)}
.combo-list li:hover,.combo-list li.hl{background:#f4eef0}
.combo-list li[aria-selected="true"]{font-weight:var(--weight-semi);color:var(--wine)}
.combo-native{position:absolute;width:1px;height:1px;opacity:0;pointer-events:none}

/* ---------- OS-076: Access as a table, a person in the panel ---------- */
.peoplebar{display:flex;justify-content:flex-end;margin:0 0 var(--space-3)}
.peopletable td{vertical-align:middle}
.peopletable tr.current{background:#fdf6f7}
.peopletable .actions{text-align:right;white-space:nowrap}
.peopletable .chips{display:flex;flex-wrap:wrap;gap:var(--space-1)}
.circlesform{display:flex;flex-direction:column;gap:var(--space-3);margin:0 0 var(--space-4)}
.ventureline{display:flex;flex-direction:column;gap:var(--space-2);padding:0 0 var(--space-3);border-bottom:1px solid var(--line)}
.ventureline strong{font-size:var(--text-sm)}
.ventureline .chips{display:flex;flex-wrap:wrap;gap:var(--space-2);align-items:center}
.addcircle input{min-height:44px;width:148px;font-size:var(--text-base);border-style:dashed}
.sidepanel h3{font-size:var(--text-sm);margin:var(--space-4) 0 var(--space-2)}
.ventureline .chipcheck{display:inline-flex;flex-direction:row;align-items:center;gap:var(--space-2);min-height:44px;padding:0 var(--space-3);
  border:1px solid var(--line-strong);border-radius:var(--radius-pill);background:#fff;cursor:pointer;font-size:var(--text-sm)}
.ventureline .chipcheck input{margin:0}
.peopletable th,.peopletable td{padding:var(--space-3) var(--space-4)}

/* ===========================================================================
   OS-077 · One design system, applied everywhere

   A clean admin panel: neutral light background, one brand colour (wine),
   soft borders, 8–12px corners, a single light shadow, an 8px grid. Every
   reusable piece — button, field, card, table, badge, panel, navigation —
   is defined once below, with its hover, focus, disabled, loading, empty,
   error and success states, so no screen styles its own.
   =========================================================================== */
:root{
  background:#f6f6f7;
  --surface:#ffffff;
  --surface-2:#fafafb;
  --ink:#1f1d20;
  --ink-2:#4b464a;        /* body text on white: 9.4:1 */
  --ink-3:#6b6569;        /* helper text on white: 5.6:1 */
  --border:#e4e3e6;
  --border-strong:#cfcdd2;
  --brand:#5a1f2b;
  --brand-hover:#6e2a38;
  --brand-tint:#f5ecee;
  --ring:0 0 0 3px rgba(90,31,43,.18);
  --shadow:0 1px 2px rgba(20,16,24,.05);
  --shadow-pop:0 8px 24px rgba(20,16,24,.10);
}
body{background:#f6f6f7;color:#1f1d20}

/* Layout: sidebar, then the page in a centred column. */
.shell{grid-template-columns:248px minmax(0,1fr)}
aside{background:#fff;border-right:1px solid var(--border);padding:24px 16px}
.brand{font-size:var(--text-lg)}
.brand img{width:40px;height:40px;border-radius:var(--radius-sm)}
aside nav{margin-top:var(--space-5);gap:var(--space-1)}
aside nav a{padding:12px;min-height:44px;border-radius:var(--radius-sm);color:#4b464a;font-size:var(--text-sm);font-weight:var(--weight-medium)}
aside nav a:hover{background:#f4f3f5;color:#1f1d20}
aside nav a.active{background:var(--brand-tint);color:var(--brand);font-weight:var(--weight-semi)}
.identity{border-top:1px solid var(--border);padding-top:var(--space-4);margin-top:var(--space-5)}
main{max-width:1280px;padding:32px 40px}

/* Page header: kicker, title, one line of helper text, actions on the right. */
.heading{margin-bottom:var(--space-5);align-items:center}
.eyebrow{color:#6b6569;font-size:var(--text-xs)}
h1{font-size:var(--text-2xl);font-weight:var(--weight-bold);color:#1f1d20;margin:0 0 var(--space-1)}
.heading .muted{font-size:var(--text-base);color:#6b6569}
h2{font-size:var(--text-base);color:#1f1d20}
h3{font-size:var(--text-sm);color:#1f1d20}
.muted{color:#6b6569}
small.hint,.hint{font-size:var(--text-xs);color:#6b6569;font-weight:var(--weight-normal)}

/* Cards and panels. */
.card,.ovstat,.ovtable,.sidepanel,.taskcard{box-shadow:var(--shadow)}
.card{border:1px solid var(--border);border-radius:var(--radius-md);padding:24px;margin:0 0 var(--space-4)}
.card h2{margin:0 0 var(--space-3)}
.sidepanel{border:1px solid var(--border);border-radius:var(--radius-md);padding:24px}
.ovstat,.ovtable,.ovdue{border-color:var(--border);border-radius:var(--radius-md)}

/* Buttons: primary, secondary, ghost, danger — one shape. */
button,.button{border:1px solid #858086;background:#fff;color:#1f1d20;border-radius:var(--radius-sm);
  padding:10px 16px;min-height:44px;font-size:var(--text-sm);font-weight:var(--weight-semi);
  transition:background-color .15s,border-color .15s,color .15s}
button:hover,.button:hover{background:#f4f3f5;border-color:#6b6569}
button:active,.button:active{transform:translateY(1px)}
.primary,button.primary,.button.primary{background:var(--brand);border-color:var(--brand);color:#fff}
.primary:hover,button.primary:hover,.button.primary:hover{background:var(--brand-hover);border-color:var(--brand-hover)}
button.ghost{background:transparent;border-color:transparent;color:#4b464a}
button.ghost:hover{background:#f4f3f5;border-color:transparent;color:#1f1d20}
button.danger{background:#b42318;border-color:#b42318;color:#fff}
button.danger:hover{background:#912018}
button:disabled,.button[aria-disabled=true]{opacity:.5;cursor:not-allowed;transform:none}
button[aria-busy=true]{cursor:progress;opacity:.75}
button.linkish{border:0;background:none;padding:0;color:var(--brand);font-weight:var(--weight-semi)}
button.linkish:hover{background:none;text-decoration:underline}
button:focus-visible,.button:focus-visible,a:focus-visible{outline:none;box-shadow:var(--ring)}

/* Fields: label above, helper below, one focus ring, an error colour. */
label{gap:var(--space-2);font-size:var(--text-sm);font-weight:var(--weight-semi);color:#1f1d20}
input,select,textarea{border:1px solid #858086;border-radius:var(--radius-sm);padding:10px 12px;min-height:44px;background:#fff;
  transition:border-color .15s,box-shadow .15s}
input:hover,select:hover,textarea:hover{border-color:#6b6569}
input:focus,select:focus,textarea:focus{outline:none;border-color:var(--brand);box-shadow:var(--ring)}
input:disabled,select:disabled,textarea:disabled{background:#f4f3f5;color:#6b6569;cursor:not-allowed}
input::placeholder,textarea::placeholder{color:#8a858a}
input[aria-invalid=true],select[aria-invalid=true],textarea[aria-invalid=true]{border-color:#b42318;box-shadow:0 0 0 3px rgba(180,35,24,.12)}

/* Tables. */
.ac-table th,.ac-table td{padding:12px 16px;border-bottom:1px solid var(--border);vertical-align:middle}
.ac-table th:first-child,.ac-table td:first-child{padding-left:16px}
.ac-table th{background:#fafafb;color:#6b6569;font-size:var(--text-xs);font-weight:var(--weight-semi);text-transform:uppercase;letter-spacing:.04em}
.ac-table tbody tr:hover{background:#fafafb}
.ac-table tbody tr:last-child td{border-bottom:0}
.tablewrap{background:#fff;border:1px solid var(--border);border-radius:var(--radius-md);overflow-x:auto;box-shadow:var(--shadow)}

/* Badges: soft fill, no outline. */
.pill,.badge{border:0;font-size:var(--text-xs);padding:4px 8px;border-radius:var(--radius-sm)}
.pill{background:#f1f0f2;color:#4b464a}
.pill.ok{background:#e9f5ec;color:#1e6b38}
.pill.wait{background:#fdf3e1;color:#8a5a12}
.pill.bad{background:#fdecea;color:#b42318}
.chip{border-radius:var(--radius-sm)}

/* Notices: info, success, warning, error. */
.notice{border:1px solid var(--border);border-left:4px solid var(--brand);background:#fff;border-radius:var(--radius-sm);padding:12px 16px}
.notice.good{border-left-color:#1e6b38;background:#f3faf5}
.notice.warn{border-left-color:#b45309;background:#fffaf0}
.notice.bad{border-left-color:#b42318;background:#fef6f5}

/* Empty, loading. */
.empty{background:#fff;border:1px dashed var(--border-strong);border-radius:var(--radius-md);padding:24px;text-align:center}
.empty .actions{display:flex;justify-content:center;gap:var(--space-2)}
.skeleton .bone{background:linear-gradient(90deg,#efeef0,#f6f6f7,#efeef0);background-size:200% 100%;animation:satori-bone 1.2s ease-in-out infinite}
@keyframes satori-bone{from{background-position:200% 0}to{background-position:-200% 0}}
@media (prefers-reduced-motion:reduce){.skeleton .bone{animation:none}}

/* Tabs and segmented views. */
.viewtabs{border-bottom:1px solid var(--border);gap:var(--space-4)}
.viewtab{padding:12px 4px;font-size:var(--text-sm);color:#6b6569}
.viewtab:hover{background:none;color:#1f1d20}
.viewtab.active{color:var(--brand);border-bottom-color:var(--brand)}

/* Dialogs and pop-overs. */
dialog{border:1px solid var(--border);border-radius:var(--radius-md);box-shadow:var(--shadow-pop)}
dialog::backdrop{background:rgba(20,16,24,.4)}
.combo-pop,.moremenu-list{border-color:var(--border);border-radius:var(--radius-md);box-shadow:var(--shadow-pop)}

/* Section headings inside a page. */
h2.section,h3.section{font-size:var(--text-xs);color:#6b6569;margin:var(--space-6) 0 var(--space-3);padding-bottom:var(--space-2);border-bottom:1px solid var(--border)}

/* Tablet and phone: one column, the bar at the bottom. */
@media (max-width:900px){
  .shell{grid-template-columns:200px minmax(0,1fr)}
  main{padding:24px}
}
@media (max-width:650px){
  .shell{display:block}
  main{padding:16px 16px 104px}
  h1{font-size:var(--text-xl)}
  .heading{flex-wrap:wrap;gap:var(--space-3)}
  .card,.sidepanel{padding:16px}
  .boardbar{flex-direction:column;align-items:stretch}
  .boardsearch{flex:1 1 auto}
}
main{margin:0 auto}
.kanban .column{background:#f1f1f3}
.addtask{border-color:#d9d8dc;background:transparent}
.addtask:hover{background:#fff}
.stagepill.past{background:#e9f5ec;border-color:#cfe8d6}
@media (min-width:651px){ .refresh-float{top:auto;bottom:24px;right:24px;box-shadow:var(--shadow-pop)} }
@media (max-width:650px){
  .ovstats{grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--space-2)}
  .ovstat{padding:var(--space-3);min-height:0;gap:var(--space-3);justify-content:flex-start}
  .ovicon{width:40px;height:40px}
  .ovicon svg{width:20px;height:20px}
  .ovnum{font-size:var(--text-lg)}
  .ovlabel{font-size:var(--text-xs)}
  .heading [data-board="new-anywhere"],.heading [data-board="new"]{display:none}
  .ring{width:128px;height:128px}
  .ring>div{width:80px;height:80px}
}
.inlineform select{width:auto;min-width:0}

/* ---------- OS-079: one person form; a readable task beside the calendar ---------- */
.personform h3{margin:var(--space-5) 0 var(--space-2)}
.personform .chips{display:flex;flex-wrap:wrap;gap:var(--space-2)}
.personform-foot{position:sticky;bottom:0;background:#fff;padding:var(--space-3) 0 0;margin-top:var(--space-4);border-top:1px solid var(--border);display:flex;gap:var(--space-2)}
.callayout.taskopen{grid-template-columns:minmax(0,1fr) 460px}
.callayout.taskopen .calpanel{padding:var(--space-4)}
@media (max-width:1100px){
  .callayout.taskopen{grid-template-columns:1fr}
  .callayout.taskopen .calpanel{order:-1;position:static}
}


/* ---------- OS-080: the boot screen ---------- */
.boot{min-height:100dvh;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--space-3);padding:var(--space-6) var(--space-4);text-align:center;color:#4b464a}
.boot img{width:56px;height:56px;border-radius:var(--radius-md)}
.boot-name{font-size:var(--text-xl);font-weight:var(--weight-bold);letter-spacing:var(--tracking-tight);color:var(--brand)}
.boot p{margin:0;font-size:var(--text-sm)}
.boot-msg{max-width:36ch;color:#1f1d20;font-size:var(--text-base)}
.boot-bar{display:block;width:160px;height:4px;border-radius:var(--radius-pill);background:#eceaed;overflow:hidden}
.boot-bar span{display:block;width:40%;height:100%;border-radius:var(--radius-pill);background:var(--brand);animation:satori-boot 1.1s ease-in-out infinite}
@keyframes satori-boot{from{transform:translateX(-100%)}to{transform:translateX(260%)}}
@media (prefers-reduced-motion:reduce){.boot-bar span{animation:none;width:100%}}

/* ---------- OS-081: the timeline as a Gantt ---------- */
.gnav{display:flex;align-items:center;justify-content:space-between;gap:var(--space-3);flex-wrap:wrap;margin:0 0 var(--space-4)}
.gnav-left,.gnav-right{display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap}
.gperiod{display:inline-flex;align-items:center;gap:var(--space-2);padding:0 var(--space-3);min-height:44px;border:1px solid var(--border);border-radius:var(--radius-sm);background:#fff;font-size:var(--text-sm)}
.gperiod .navicon{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:1.8;color:#6b6569}
.seg{display:inline-flex;border:1px solid var(--border);border-radius:var(--radius-sm);overflow:hidden;background:#fff}
.seg button{border:0;border-radius:0;background:transparent;min-height:44px;padding:0 var(--space-4);font-size:var(--text-sm)}
.seg button.active{background:var(--brand);color:#fff}
.gngroup select{min-height:44px;width:auto;font-size:var(--text-base)}
.gantt{border:1px solid var(--border);border-radius:var(--radius-md);background:#fff;box-shadow:var(--shadow);margin:0 0 var(--space-5);overflow:hidden}
.gscroll{overflow-x:auto;overflow-y:hidden;position:relative}
.grow{display:flex;min-height:48px;border-bottom:1px solid var(--border)}
.grow:last-child{border-bottom:0}
.grow.current .gleft{background:#fdf6f7}
.gleft{display:grid;grid-template-columns:230px 60px 130px 100px;align-items:center;gap:0;flex:0 0 520px;width:520px;position:sticky;left:0;z-index:2;background:#fff;border-right:1px solid var(--border);padding:0 var(--space-3);font-size:var(--text-sm)}
.gleft>span{padding:var(--space-1) var(--space-2) var(--space-1) 0;min-width:0}
.ghead{background:#fafafb;min-height:64px;position:sticky;top:0;z-index:3}
.ghead .gleft{background:#fafafb;align-items:end;padding-bottom:var(--space-2)}
.gcols span{font-size:var(--text-xs);font-weight:var(--weight-semi);text-transform:uppercase;letter-spacing:.04em;color:#6b6569}
.gdays{position:relative;flex:0 0 auto;min-height:48px}
.gmonths{position:absolute;top:var(--space-1);left:0;right:0;height:20px;font-size:var(--text-xs);color:#6b6569;font-weight:var(--weight-semi)}
.gmonths span{position:absolute;top:0;padding:0 var(--space-2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;border-left:1px solid var(--border)}
.gday{position:absolute;top:24px;bottom:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:0;font-size:var(--text-xs);color:#6b6569;border-left:1px solid var(--border);line-height:var(--leading-tight)}
.gday b{font-size:var(--text-sm);color:#1f1d20}
.gday.weekend{background:#f6f6f7}
.gday.istoday{background:var(--brand);color:#fff;border-radius:var(--radius-sm);top:28px;bottom:4px}
.gday.istoday b,.gday.istoday small{color:#fff}
.gtoday{position:absolute;top:0;bottom:0;width:2px;background:#c58a98;z-index:1}
.gtask{display:flex;align-items:flex-start;gap:var(--space-2);min-width:0}
.gtask>span{min-width:0;display:flex;flex-direction:column}
.gtask .linkish{text-align:left;color:#1f1d20;font-weight:var(--weight-semi);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:190px;display:block}
.gtask small{font-size:var(--text-xs)}
.dot.p-urgent{background:#e25c5c}.dot.p-high{background:#ec8a6a}.dot.p-medium{background:#e9c04a}.dot.p-low{background:#9a9296}
.gtask .dot{margin-top:4px;flex:none}
.gbar{position:absolute;top:8px;height:32px;border-radius:var(--radius-sm);border:1px solid;display:flex;align-items:center;padding:0 var(--space-2);font-size:var(--text-sm);font-weight:var(--weight-medium);cursor:grab;user-select:none;touch-action:none;box-sizing:border-box;z-index:1}
.gbar.dragging{cursor:grabbing;opacity:.85;box-shadow:var(--shadow-pop);z-index:4}
.gbar-text{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;pointer-events:none}
.gbar-handle{position:absolute;right:0;top:0;bottom:0;width:12px;cursor:ew-resize;border-radius:0 var(--radius-sm) var(--radius-sm) 0}
.gbar-handle:hover{background:rgba(0,0,0,.08)}
.gbar.s-open{background:#fdf3e1;border-color:#e9cf8c;color:#7a5410}
.gbar.s-in_progress{background:#e3f1e7;border-color:#9fd3ae;color:#1e6b38}
.gbar.s-submitted{background:#fbeccb;border-color:#e3b64a;color:#7a5410}
.gbar.s-completed,.gbar.s-cancelled{background:#ececef;border-color:#c9c6cc;color:#6b6569}
.gbar.p-urgent{border-left:4px solid #e25c5c}.gbar.p-high{border-left:4px solid #ec8a6a}
.gmile{position:absolute;top:16px;width:16px;height:16px;margin-left:-8px;background:#8a8086;transform:rotate(45deg);border-radius:var(--radius-mark);cursor:grab;touch-action:none;z-index:1}
.gmile.p-urgent{background:#e25c5c}.gmile.p-high{background:#ec8a6a}.gmile.p-medium{background:#e9c04a}
.gmile-label{position:absolute;left:24px;top:-4px;transform:rotate(-45deg);transform-origin:0 0;white-space:nowrap;font-size:var(--text-sm);font-weight:var(--weight-medium);color:#1f1d20;pointer-events:none;margin-left:4px}
.ggroup{background:#fafafb;min-height:40px}
.ggroup .gleft{display:block;font-weight:var(--weight-semi);font-size:var(--text-sm);padding:var(--space-2) var(--space-3);background:#fafafb}
.gempty .gleft{display:block;padding:var(--space-4) var(--space-3)}
.gtable-title{font-size:var(--text-lg);margin:0 0 var(--space-3)}
.gtable td{white-space:nowrap}
.overdue-strip .chips{display:flex;flex-wrap:wrap;gap:var(--space-2);margin:0}
.overdue-strip .chip,.card .chips button.chip{border:1px solid #e8c3ba;background:#fff;min-height:44px;padding:0 var(--space-3);border-radius:var(--radius-sm);font-weight:var(--weight-medium);color:#1f1d20}
.stagechip.s-in_progress{background:#e3f1e7;color:#1e6b38}
.stagechip.s-completed{background:#ececef;color:#6b6569}
.stagechip.s-submitted{background:#fbeccb;color:#7a5410}
.stagechip.s-cancelled{background:#fdecea;color:#b42318}
@media (max-width:720px){
  .gleft{grid-template-columns:1fr;flex-basis:200px;width:200px}
  .gleft .gmeta{display:none}
  .gtask .linkish{max-width:150px}
}
.gtask .linkish{text-decoration:none}
.gtask .linkish:hover{text-decoration:underline}
.gleft{grid-template-columns:220px 56px 144px 100px}

/* ---------- OS-082: skeletons ---------- */
.skeleton{display:block;grid-auto-flow:unset;overflow:visible}
.skeleton .bone{display:block;height:12px;border-radius:var(--radius-mark);margin:0 0 var(--space-2)}
.skeleton .bone.round{width:40px;height:40px;border-radius:50%;flex:none}
.skeleton .ovstat{display:flex;gap:var(--space-3);align-items:center}
.skeleton .ovstat>span:last-child{flex:1 1 auto;display:block}
.skeleton table .bone{margin:0}

/* ---------- OS-084: the list as one table; stage colours ---------- */
.listsum{display:flex;align-items:center;gap:var(--space-3);margin:0 0 var(--space-3);font-size:var(--text-base)}
.listwrap{margin:0 0 var(--space-4)}
.listtable{table-layout:fixed}
.listtable th:nth-child(1){width:38%}.listtable th:nth-child(2){width:14%}.listtable th:nth-child(3){width:16%}.listtable th:nth-child(4){width:10%}.listtable th:nth-child(5){width:10%}.listtable th:nth-child(6){width:12%}
.listtable td{height:72px;padding:var(--space-2) var(--space-4);vertical-align:middle}
.lrow{cursor:pointer}
.lrow.current td{background:#fdf6f7}
.lrow.done .name strong{color:#6b6569}
.listtable .name strong{display:block;font-size:var(--text-base);line-height:var(--leading-snug)}
.clamp1{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-size:var(--text-sm)}
.lgroup td{background:#fafafb;height:48px;padding:0 var(--space-4)}
.lgroup-line{display:flex;align-items:center;justify-content:space-between;gap:var(--space-2)}
.lgroup-toggle{border:0;background:transparent;padding:0 var(--space-2) 0 0;min-height:44px;display:inline-flex;align-items:center;gap:var(--space-2);font-weight:var(--weight-semi);font-size:var(--text-base);color:#1f1d20}
.lgroup-toggle:hover{background:transparent;color:var(--brand)}
.lgroup-toggle .chev{width:16px;display:inline-block;color:#6b6569}
.lgroup .count{background:#ececef;border-radius:var(--radius-pill);padding:0 var(--space-2);font-size:var(--text-xs);min-width:20px;min-height:20px;display:inline-flex;align-items:center;justify-content:center;line-height:var(--leading-snug)}
.sdot{width:10px;height:10px;border-radius:50%;display:inline-block;background:#8a8086}
.sdot.doing{background:#2d6cdf}.sdot.review{background:#d98e04}.sdot.done{background:#1e8a44}.sdot.cancelled{background:#b42318}
.rowacts{display:inline-flex;align-items:center;gap:var(--space-1);justify-content:flex-end;width:100%}
.rowacts .act{white-space:nowrap;font-size:var(--text-sm);padding:0 var(--space-3);min-height:44px}
.rowacts .act.primary{background:var(--brand);color:#fff;border-color:var(--brand)}
.duecell{display:inline-block;line-height:var(--leading-snug);font-size:var(--text-sm)}
.duecell.islate .due-over{color:#b42318;font-weight:var(--weight-semi)}
.listtable .moremenu-list{right:0}
/* Each stage its own colour, everywhere. */
.stagedot{border-color:#8a8086}
.stagedot.doing{border-color:#2d6cdf}.stagedot.review{border-color:#d98e04}.stagedot.done{border-color:#1e8a44}
.stagechip{background:#ececef;color:#4b464a}
.stagechip.s-in_progress{background:#e4edfb;color:#1d4fa8}
.stagechip.s-submitted{background:#fdf1dc;color:#8a5a12}
.stagechip.s-completed{background:#e6f4ea;color:#1e6b38}
.gbar.s-open{background:#f1f0f2;border-color:#cfcdd2;color:#4b464a}
.gbar.s-in_progress{background:#e4edfb;border-color:#9dbdf0;color:#1d4fa8}
.gbar.s-submitted{background:#fdf1dc;border-color:#ebc97a;color:#8a5a12}
.gbar.s-completed{background:#e6f4ea;border-color:#a9d9b7;color:#1e6b38}
.stagepill.now{background:#e4edfb;border-color:#9dbdf0;color:#1d4fa8}
@media (max-width:900px){
  .listtable{table-layout:auto}
  .listtable th:nth-child(2),.listtable td:nth-child(2),.listtable th:nth-child(3),.listtable td:nth-child(3){display:none}
}
.listtable th:nth-child(1){width:30%}.listtable th:nth-child(3){width:14%}.listtable th:nth-child(6){width:22%}
.rowacts{flex-wrap:wrap}
.rowacts .moremenu>summary{min-width:44px;padding:0}

.listtable td.actions{overflow:visible;text-align:right}

/* ---------- OS-085: pictures and the profile ---------- */
.avatar.img{padding:0;overflow:hidden;background:#ececef}
.avatar.img img{width:100%;height:100%;object-fit:cover;display:block;border-radius:50%}
.avatar.big{width:80px;height:80px;font-size:var(--text-xl)}
.me{display:flex;align-items:center;gap:var(--space-3);text-decoration:none;color:inherit;min-height:44px;border-radius:var(--radius-sm)}
.me:hover{background:#f4f3f5}
.me .avatar{width:36px;height:36px;flex:none}
.me>span{display:flex;flex-direction:column;min-width:0}
.me .me-name{font-weight:var(--weight-semi);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.me .me-sub{color:#6b6569;font-size:var(--text-xs)}
.profilecard{display:flex;gap:var(--space-4);align-items:flex-start;background:#fff;border:1px solid var(--border);border-radius:var(--radius-md);padding:var(--space-4);margin:0 0 var(--space-4)}
.profilecard-body{flex:1 1 auto;min-width:0}
.profilecard .inlineform{align-items:flex-end}
.profilecard .inlineform .grow{flex:1 1 auto}
.profile-actions{display:flex;flex-wrap:wrap;gap:var(--space-2);margin:0}
.filebtn{position:relative;overflow:hidden;cursor:pointer}
.filebtn input{position:absolute;inset:0;opacity:0;cursor:pointer;min-height:44px}
@media (max-width:650px){.profilecard{flex-direction:column;align-items:center;text-align:center}.profilecard .inlineform{flex-direction:column;align-items:stretch}}
.notefiles{display:flex;flex-wrap:wrap;gap:var(--space-2);align-items:center;margin:0}
.notefiles .chip{display:inline-flex;align-items:center;gap:var(--space-1)}
.attachments{display:flex;flex-wrap:wrap;gap:var(--space-2);margin:var(--space-2) 0 0}
.attach{display:inline-flex;align-items:center;gap:var(--space-1);min-height:44px;padding:0 var(--space-3);border:1px solid var(--border);border-radius:var(--radius-sm);background:#fff;color:#1f1d20;text-decoration:none;font-size:var(--text-sm)}
.attach:hover{border-color:var(--brand)}
.attach.img{padding:0;overflow:hidden;width:96px;height:96px}
.attach.img img{width:100%;height:100%;object-fit:cover;display:block}

/* ---------- OS-087: the profile card; All tasks icons and stage tints ---------- */
.profilecard{display:grid;grid-template-columns:96px minmax(0,1fr);gap:var(--space-4) var(--space-5);align-items:start;padding:var(--space-5);margin:0 0 var(--space-5)}
.avatar.big{width:96px;height:96px;font-size:var(--text-2xl)}
.profile-name{display:flex;gap:var(--space-2);align-items:flex-end;margin:0 0 var(--space-3)}
.profile-name label{flex:1 1 auto;min-width:0}
.profile-facts{grid-column:1/-1;display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:var(--space-3);margin:0;padding-top:var(--space-4);border-top:1px solid var(--border)}
.profile-facts dt{font-size:var(--text-xs);text-transform:uppercase;letter-spacing:var(--tracking-kicker);color:#6b6569;font-weight:var(--weight-semi);margin:0 0 var(--space-1)}
.profile-facts dd{margin:0;font-size:var(--text-base)}
@media (max-width:650px){.profilecard{grid-template-columns:1fr;justify-items:center;text-align:center}.profile-name{flex-direction:column;align-items:stretch;width:100%}.profile-actions{justify-content:center}}
.viewtab{display:inline-flex;align-items:center;gap:var(--space-2)}
.viewtab .navicon{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.kanban .column{border-top:3px solid #c9c6cc}
.kanban .column.stage-doing{border-top-color:#2d6cdf}
.kanban .column.stage-review{border-top-color:#d98e04}
.kanban .column.stage-done{border-top-color:#1e8a44}
.kanban .column.over{border-top-color:var(--brand)}
.column-head .count{border-radius:var(--radius-pill);padding:0 var(--space-2);min-width:24px;min-height:24px;display:inline-flex;align-items:center;justify-content:center;font-size:var(--text-xs);background:#ececef;color:#4b464a}
.column-head .count.badge-doing{background:#e4edfb;color:#1d4fa8}
.column-head .count.badge-review{background:#fdf1dc;color:#8a5a12}
.column-head .count.badge-done{background:#e6f4ea;color:#1e6b38}
.taskcard .avatar,.listtable .avatar{width:32px;height:32px}
.taskcard .avatar.img img,.listtable .avatar.img img{width:32px;height:32px}
.profile-facts dd{overflow-wrap:anywhere}

/* ---------- OS-088: status marks; compact columns; panel as a sheet on tablets and phones ---------- */
.sicon{flex:none;display:inline-block;vertical-align:middle}
.sicon.s-todo{color:#8a8590}.sicon.s-doing{color:#2d6cdf}.sicon.s-review{color:#d98e04}.sicon.s-done{color:#1e8a44}.sicon.s-cancelled{color:#b42318}
.stagechip::before{display:none}
.stagechip{gap:var(--space-1)}
.stagechip .sicon{width:12px;height:12px}
.kanban .column{border-top:0;border-radius:var(--radius-sm);padding:var(--space-2);background:#f1f1f3}
.column-head{gap:var(--space-2);margin:0 0 var(--space-2);padding:var(--space-1) var(--space-1) var(--space-1) var(--space-2);min-height:44px}
.column-head h2{font-size:var(--text-base);font-weight:var(--weight-semi);margin:0}
.column-head .count{margin-left:0;background:#e4e3e6;color:#4b464a;font-weight:var(--weight-medium)}
.column-head .colplus{margin-left:auto;min-width:44px;min-height:44px;font-size:var(--text-lg);color:#6b6569;border-radius:var(--radius-sm)}
.column-head .colplus:hover{background:#e4e3e6;color:#1f1d20}
.column-body{gap:var(--space-2)}
.taskcard{border-radius:var(--radius-sm);padding:var(--space-3)}
.addtask{display:none}
@media (max-width:1100px){
  /* A tapped task opens over the screen rather than above it, out of view. */
  .withpanel .sidepanel{position:fixed;inset:0;z-index:50;border-radius:0;border:0;max-height:none;height:100dvh;overflow-y:auto;padding:var(--space-4) var(--space-4) var(--space-7);order:0;-webkit-overflow-scrolling:touch}
  .callayout.taskopen .calpanel{position:fixed;inset:0;z-index:50;border-radius:0;border:0;height:100dvh;overflow-y:auto;padding:var(--space-4) var(--space-4) var(--space-7)}
  .withpanel .sidepanel .taskpanel>header,.calpanel .taskpanel>header{position:sticky;top:calc(-1 * var(--space-4));background:#fff;z-index:1;padding-top:var(--space-2)}
}

/* The full menu as a drawer on a phone (OS-088). */
.navmenu{border:0;background:transparent;min-height:44px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:0;font-size:var(--text-xs);color:#4b464a;padding:var(--space-1);font-weight:var(--weight-medium)}
.navmenu .navicon{width:22px;height:22px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round}
.navscrim{display:none}
@media(max-width:650px){
  body.navopen .shell>aside{display:block;position:fixed;top:0;bottom:0;left:0;width:280px;max-width:85vw;z-index:60;overflow-y:auto;box-shadow:var(--shadow-pop)}
  body.navopen .navscrim{display:block;position:fixed;inset:0;background:rgba(20,16,24,.4);z-index:55}
}
@media (max-width:1100px){.refresh-float{z-index:40}.withpanel .sidepanel,.callayout.taskopen .calpanel{z-index:70}}
/* Owner, 13 Oct: keep the coloured top edge on every column, and the
   "+ Add task" at the foot as well as the + in the header. */
.kanban .column{border-top:4px solid #a9a5ab;border-radius:var(--radius-md) var(--radius-md) var(--radius-sm) var(--radius-sm)}
.kanban .column.stage-todo{border-top-color:#8a8590}
.kanban .column.stage-doing{border-top-color:#2d6cdf}
.kanban .column.stage-review{border-top-color:#d98e04}
.kanban .column.stage-done{border-top-color:#1e8a44}
.kanban .column.over{border-top-color:var(--brand)}
.column-head .count{background:#e4e3e6;color:#4b464a}
.stage-doing .column-head .count{background:#e4edfb;color:#1d4fa8}
.stage-review .column-head .count{background:#fdf1dc;color:#8a5a12}
.addtask{display:flex;align-items:center;justify-content:center;gap:var(--space-1);width:100%;min-height:44px;border:1px dashed #cfcdd2;background:transparent;color:#6b6569;border-radius:var(--radius-sm);font-weight:var(--weight-medium)}
.addtask:hover{background:#fff;border-color:#a9a5ab;color:#1f1d20}

/* ---------- OS-089: your day ---------- */
.yourday{margin:0 0 var(--space-5)}
.daytiles{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--space-3);margin:0 0 var(--space-4)}
.daytile{display:flex;flex-direction:column;gap:var(--space-1);padding:var(--space-4);background:#fff;border:1px solid var(--border);border-left:4px solid #c9c6cc;border-radius:var(--radius-md);text-decoration:none;color:inherit;min-height:44px;box-shadow:var(--shadow)}
.daytile:hover{border-color:var(--border-strong)}
.daytile.today{border-left-color:#2d6cdf}.daytile.late{border-left-color:#b42318}.daytile.review{border-left-color:#d98e04}.daytile.doing{border-left-color:#1e8a44}
.daytile.late.has{background:#fef6f5}
.daynum{font-size:var(--text-2xl);font-weight:var(--weight-bold);line-height:var(--leading-tight);font-variant-numeric:tabular-nums}
.daylabel{font-size:var(--text-sm);color:#4b464a}
.daylist{list-style:none;margin:0;padding:0;background:#fff;border:1px solid var(--border);border-radius:var(--radius-md);box-shadow:var(--shadow)}
.dayrow{display:grid;grid-template-columns:20px minmax(0,1fr) auto auto;align-items:center;gap:var(--space-3);padding:var(--space-3) var(--space-4);min-height:64px;border-bottom:1px solid var(--border);cursor:pointer}
.dayrow:last-child{border-bottom:0}
.dayrow:hover{background:#fafafb}
.daymain{display:flex;flex-direction:column;min-width:0}
.daymain strong{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.daymain .muted{font-size:var(--text-sm)}
.daywhen{font-size:var(--text-sm);color:#4b464a;white-space:nowrap}
.daywhen.islate{color:#b42318;font-weight:var(--weight-semi)}
.dayact .act{min-height:44px;font-size:var(--text-sm);padding:0 var(--space-3);white-space:nowrap}
.dayempty{margin:0;padding:var(--space-4);background:#fff;border:1px dashed var(--border-strong);border-radius:var(--radius-md);color:#4b464a}
@media (max-width:900px){.daytiles{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:650px){.dayrow{grid-template-columns:20px minmax(0,1fr);row-gap:var(--space-1)}.daywhen,.dayact{grid-column:2}.dayact{justify-self:start}}
