/* ===================================================================
   sholo — the marketing site.

   Deliberately separate from the app's stylesheet. They share a look,
   not a codebase: the app's CSS is 3,000 lines carrying the floor plan,
   the gear cards and the run of show, and importing all of that to draw
   a landing page would make the page pay for the app.

   THE SHAPE

   Full-bleed dark bands, very large type, very few words. It works here
   for a reason that isn't just fashion: the app is dark-themed, so a
   screenshot of a floor plan bleeds into a black page instead of sitting
   in a bordered card looking like a document. The product IS the
   picture, which is the only condition under which this layout works.

   NO JAVASCRIPT, STILL

   The scroll reveals are CSS — `animation-timeline: view()`, wrapped in
   an @supports so a browser without it simply shows the content. A
   landing page that needs a script to display its own text is a landing
   page that shows nothing on a bad hotel connection, which is exactly
   where this gets opened.
=================================================================== */

/* ---- READABILITY, AND WHY PURE BLACK WAS THE PROBLEM ---------------

   TJ: "The text copy is hard to read. Dark grey on black is tough for
   humans to read."

   He is right, and the contrast ratio said otherwise: #9297a0 on #000
   measures 7.16:1, which passes WCAG AAA. That is the lesson worth
   keeping — a page can clear the numeric bar and still be tiring, and
   the number is not the thing being measured.

   What was actually happening, in order of how much it mattered:

     PURE BLACK. Light text on #000 halates — the glyphs bleed into the
     background, hardest for the very large number of people with any
     astigmatism, and worst on the OLED screens this trade carries. Off
     black fixes most of it and costs nothing. Note that the APP has
     never used #000; it is #151515. The marketing site was the outlier,
     so this also makes the site look like the product.

     84 CHARACTERS A LINE. 760px at 18px. Comfortable is 45-75; past
     about 85 the eye loses its place tracking back to the left margin,
     which reads as "hard to read" without anybody being able to say
     why. See .narrow.

     THE GREY ITSELF, which is the part everybody reaches for first and
     the smallest of the three.

   --panel moves with the background so cards keep the same separation
   from it they had before. */
:root {
  --bg: #0c0e11;
  --panel: #17191d;
  --panel2: #1b1d20;
  --border: #2a2d31;
  --text: #f5f5f7;
  /* Labels, captions, nav — things read in a glance rather than in
     paragraphs. Body copy has its own, brighter, below. */
  --muted: #9297a0;
  /* Prose. 10.4:1 on the background, and the gap to --text still puts a
     heading clearly above the text under it. */
  --body: #b9bec7;
  --accent: #4f8cff;
  --accent-soft: rgba(79, 140, 255, .14);
  --ok: #35c26b;
  /* Amber means "look here" and nothing else in this product. It stays a
     fixed color rather than following a customer's brand, because the
     ring around the cue being called can't be the accent when the accent
     is somebody's corporate blue. */
  --warn: #f2b134;
  /* A department's color, as raw channels so it can be tinted at any
     alpha. Declared here as a neutral so a cell that somehow arrives
     without a department class renders gray instead of dropping the
     whole background declaration on an invalid var(). */
  --rc: 130, 140, 155;
  --max: 1400px;
  --pad: 24px;
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  /* The system stack. A webfont is 40–100KB and a flash of nothing
     while it loads, to render eight seconds of reading. */
  font: 17px/1.6 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
        "Helvetica Neue", Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a { color: var(--accent); text-decoration: none; }

.wrap { max-width: var(--max); margin: 0 auto; padding: 0 var(--pad); }
/* 1120, AND THE TYPE SCALES WITH IT.

   TJ, twice now: "You can widen the copy width. It looks weird with so
   much empty space on the sides," and then, at 860 on a 2000px
   monitor: "We can widen the copy quite a bit. We are wasting half the
   page."

   The first time I went 680 -> 860 and thought that was the answer. It
   was a third of one. 860 on a wide display leaves more empty page than
   column, which is what he is looking at.

   THE CEILING IS CHARACTERS, NOT PIXELS, and the check below enforces
   it: at 0.46em a character — measured in a browser against this font
   stack, not the optimistic 0.5 rule of thumb — 1120px needs 27px type
   to stay at 90 characters a line, which is where 860/22 already was.
   Widening without growing the type is the version TJ called hard to
   read at 760/18, so the two numbers move together or not at all.

   There is a floor on how much of a wide screen a single column of
   prose can honestly fill. Past about 92 characters a line the eye
   loses the return sweep and the page gets harder to read, not fuller.
   What fills the rest is the diagram and the device demo, which are
   not prose and are sized on their own. */
.narrow { max-width: 1120px; }
.center { text-align: center; margin-left: auto; margin-right: auto; }

/* ---- sticky nav ----
   Thin, and it gets out of the way. The blur is the only decorative
   effect on the page and it earns its place: without it, text scrolling
   under a translucent bar is unreadable for the second it takes to
   pass. */
.top {
  position: sticky;
  top: 0;
  z-index: 20;
  background: rgba(0, 0, 0, .72);
  backdrop-filter: saturate(160%) blur(18px);
  -webkit-backdrop-filter: saturate(160%) blur(18px);
  border-bottom: 1px solid rgba(255, 255, 255, .08);
}
.top-inner {
  max-width: var(--max);
  margin: 0 auto;
  padding: 0 var(--pad);
  height: 60px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.brand { display: flex; align-items: center; }
/* The full lockup, strapline and all.

   It reads "sholo — show logistics", and the second line is the part
   that separates this from every other four-letter wordmark, so cropping
   it to save 15px of bar height was the wrong trade.

   34px is the floor. The strapline is about 16% of the lockup's height,
   which puts it near 5.5px of CSS — fine on a retina screen where that's
   11 real pixels, and the reason the bar grew to 60px rather than
   staying at 48. Any smaller and it stops being words. */
.brand img { height: 34px; width: auto; display: block; }
@media (max-width: 620px) { .brand img { height: 28px; } }

.top nav { display: flex; align-items: center; gap: 4px; }
/* :not(.btn) is load-bearing.

   `.top nav a` is two classes and an element; `.btn-primary` is one
   class. So this rule outranked the button's own color and padding, and
   the Get started button came out gray-on-blue with the wrong box — a
   contrast failure on the one control the page exists to get pressed.

   Scoping the rule to links that aren't buttons is better than fighting
   it with !important: the button keeps owning how a button looks. */
.top nav a:not(.btn) { color: var(--muted); font-size: 14px; padding: 8px 10px; }
.top nav a:not(.btn):hover { color: var(--text); }

/* ---- buttons ---- */
.btn {
  display: inline-block;
  padding: 13px 26px;
  border-radius: 980px;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text);
  font-size: 16px;
  font-weight: 500;
  cursor: pointer;
  font-family: inherit;
}
.btn:hover { border-color: #4a4e55; }
.btn-primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.btn-primary:hover { filter: brightness(1.1); }
.btn-sm { padding: 7px 16px; font-size: 14px; }
.btn-row { display: flex; flex-wrap: wrap; gap: 14px; }
.center .btn-row { justify-content: center; }

/* ---- type ----
   The whole look is here. Big, tight, and few words — a headline that
   needs three lines on a laptop isn't a headline. */
/* A HEADLINE TAKES A WIDER MEASURE THAN BODY COPY.

   TJ: "on sholo.app/company the title text needs to fit on just 2 lines,
   not 3."

   It was three because the hero h1 sat in .narrow — the 860px column
   sized for PROSE, where the ceiling is about 92 characters a line and
   the whole point is a short measure. At 88px that column holds roughly
   nineteen characters, so a nine-word headline could not fit in two
   lines however it was broken. A forced <br> would not have helped;
   the container was the constraint.

   1040px is the width .device-ipad and .bleed-pan already use, so the
   headline lines up with the biggest picture on the page rather than
   introducing a fourth measure.

   The lede stays narrow. It is prose, it is what the 860 was for, and
   letting it run to 1040 would push it past the reading width the
   contrast and chars-per-line checks exist to protect. */
.band-hero .narrow { max-width: 1280px; }
.band-hero .lede { max-width: 900px; margin-left: auto; margin-right: auto; }

h1 {
  font-size: clamp(40px, 7.5vw, 88px);
  line-height: 1.05;
  letter-spacing: -.03em;
  font-weight: 600;
  margin: 0 0 22px;
}
h2 {
  font-size: clamp(30px, 4.6vw, 56px);
  line-height: 1.08;
  letter-spacing: -.025em;
  font-weight: 600;
  margin: 0 0 20px;
}
h3 { font-size: 20px; font-weight: 600; letter-spacing: -.01em; margin: 0 0 8px; }

.lede {
  font-size: clamp(19px, 2.2vw, 29px);
  line-height: 1.45;
  color: var(--muted);
  margin: 0 0 30px;
}
/* :not([class]) is load-bearing, for the same reason `:not(.btn)` is up
   in the nav — and this one shipped.

   `.band p` is two classes and an element. `.caption`, `.plan-note` and
   `.lede` are one class each, so this rule outranked all three and
   replaced their `margin: X auto` with `margin: 0 0 18px`. Losing the
   `auto` is what broke the page: a centered block with a max-width and
   no auto margins doesn't center, it pins to the left. Every caption on
   the site sat hard against the left edge in a narrow ragged column, and
   it read as broken layout rather than as one wrong word in one rule.

   Scoping this to paragraphs with NO class at all means any paragraph
   that has been given a name owns its own spacing, and this rule can
   only ever style the plain ones. */
/* Body copy. Brighter than a caption, a point larger than it was, and
   with the gap between paragraphs opened past the line spacing (1.6 of
   19px is 30px, so 18px of margin made paragraph breaks read as line
   breaks). */
.band p:not([class]) { color: var(--body); font-size: 27px; margin: 0 0 32px; }
.band p strong { color: var(--text); font-weight: 600; }
.eyebrow {
  font-size: 15px;
  font-weight: 600;
  color: var(--accent);
  letter-spacing: .01em;
  margin: 0 0 14px;
}

/* ---- bands ----
   Full width, generous height. Each one is a single idea. */
.band { padding: clamp(52px, 7vw, 92px) 0; }
.band-hero { padding: clamp(40px, 5vw, 64px) 0 0; }
.band-line { border-top: 1px solid var(--border); }

/* ---- A BAND WITH SEVERAL HEADINGS IN IT ----------------------------

   The home page runs ONE h2 per band, with 92px of padding above and
   below it. At 56px that reads as a section title, which is what it is.

   The landing pages stack seven h2s inside a single band, and they
   inherited the same 56px with margin-top: 0 — so the only space above
   a heading was the 30px bottom margin of the paragraph before it. A
   section break and a paragraph break were the same size, four of the
   seven wrapped onto two lines, and the page read as a stack of
   billboards with grey fine print between them. Measured in a browser
   rather than guessed at: 88px h1, 56px h2, 24px body.

   These are SUBHEADS, not section titles, so they get subhead size and
   a real gap above. 38px at the top of the clamp fits every one of them
   on a single line. */
/* 34, not 38. The reading column narrowed from 760 to 680 for the sake
   of the prose, and 38px put the longest heading on this site — "The
   called cue holds the top of the screen" — back onto two lines. Every
   h2 across the four pages was measured in the real column: 36 wraps
   that one, 35 is the first that fits, 34 leaves a heading's worth of
   headroom for the next one somebody writes.

   Back up to 42 now the column is 860 — every heading was re-measured
   at the new width and none of them wraps, including the longest. A
   subhead has to stay clearly under the 88px h1 and clearly over the
   22px body, and 42 sits between them. */
.band-guide h2 {
  font-size: clamp(25px, 3vw, 42px);
  line-height: 1.15;
  margin-top: clamp(52px, 6vw, 80px);
}
/* The first one already has the band's own padding above it, and a
   second gap there reads as a missing image rather than as spacing. */
.band-guide .wrap:first-of-type h2:first-child { margin-top: 0; }

/* ---- full-bleed media ----
   Edge to edge, no frame. A screenshot in a bordered card reads as a
   document about the product; the same screenshot bleeding off both
   sides reads as the product. */
.bleed { width: 100%; margin: clamp(28px, 4vw, 52px) 0 0; }
.bleed img { display: block; width: 100%; height: auto; }
.bleed-inset { max-width: 1280px; margin-left: auto; margin-right: auto; padding: 0 var(--pad); }

/* ---- a picture you pan, on a screen too small to hold it ----
   Every other image on this site is a phone screenshot, which shrinks
   to a phone perfectly well because it was one. The org chart is not:
   it is an iPad screen of forty labelled faces, and at 360px wide the
   faces are 30px across and the names are gone.

   That would reproduce, on the page selling the chart, the exact
   problem the picture was added to fix — TJ: "no one knows what you are
   talking about." A picture too small to read is a picture that hasn't
   been shown.

   So below the breakpoint it keeps a width its labels survive at and
   the box scrolls sideways under a thumb, the way .rail already does
   further down this file. Which is also what the chart does in the app:
   you pan it. That rhyme is a bonus — the reason is legibility.

   THE FLOOR IS TIED TO THE PICTURE, not to a round number. The chart is
   1600px of source carrying 28px labels, so at 860 they land near 15px
   and at the 620 this started on they land near 9px, which is present
   without being readable — the worst of both, since it costs the bytes
   and answers nothing. The breakpoint sits just above the floor so the
   scroller disappears the moment the column can hold the image outright. */
.bleed-pan { overflow-x: auto; overscroll-behavior-x: contain; }
.bleed-pan img { min-width: 860px; }
@media (min-width: 920px) { .bleed-pan img { min-width: 0; } }

/* AND IT HAS TO LOOK LIKE A SCREEN, NOT A SLAB. TJ, on the first one
   live: "it doesn't look good."

   He was right, and the reason is that this is the only picture on the
   site that carries its own background. Every phone and tablet shot
   sits inside .device-screen, which supplies the bezel and the radius;
   plan-pins.png needs neither because its background is pure black and
   simply merges into the page. This one is the app's dark grey on the
   site's near-black, full width, with four square corners — a rectangle
   of not-quite-the-same-colour pasted over the layout.

   The radius and the hairline are what .device-screen was already doing
   for everything else; this just does it without pretending there is a
   tablet around it.

   AND IT IS NARROWER THAN THE BLEED. At 1280 it ran 1232px against an
   860px column of text, so the picture dwarfed the argument it was
   supporting. 1040 is the width .device-ipad already uses further down,
   which is the site's existing answer to "how wide is a big picture." */
.bleed-pan { max-width: 1040px; }
.bleed-pan img {
  border-radius: 14px;
  border: 1px solid var(--border);
}
.bleed-pan:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 14px; }

/* A placeholder that says what it's waiting for. A blank frame on a
   staging site is indistinguishable from an image that failed to load,
   and this page gets looked at before the pictures exist. */
.shot-placeholder {
  aspect-ratio: 16 / 9;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  text-align: center;
  padding: 24px;
  color: var(--muted);
  border: 1px solid var(--border);
  border-radius: 18px;
  background:
    repeating-linear-gradient(45deg, #0c0d0f 0 14px, #131518 14px 28px);
}
.shot-placeholder b { color: var(--text); font-size: 18px; font-weight: 600; }
.shot-placeholder span { font-size: 15px; max-width: 46ch; }
.caption {
  margin: 16px auto 0;
  max-width: 62ch;
  color: var(--muted);
  font-size: 15px;
  text-align: center;
  padding: 0 var(--pad);
}

/* ---- a device, drawn rather than photographed ----
   A frame composited into a PNG is a frame at one size: soft on a retina
   display, and it can't contain anything you can touch. This one is CSS,
   so it stays sharp at any zoom, adds no bytes, and the screen inside it
   is a live scroll container. */
.device {
  position: relative;
  margin: clamp(28px, 4vw, 52px) auto 0;
  padding: 9px;
  border-radius: 30px;
  /* Nearly black, barely a gradient.

     The first version was mid-gray with a bright rim, which is what a
     device frame looks like in a stock mockup and nothing like what one
     looks like on a black page — it read as a plastic picture frame
     around the screenshot. Real hardware against a dark background is
     mostly silhouette: you see the screen, and the edge is a hint.

     The bezel also got thinner. 13px on a 700px tablet is proportionally
     about double a real iPad's, and an over-thick bezel is the single
     biggest tell that a frame was drawn rather than photographed. */
  background: linear-gradient(#2b2e33, #141517);
  box-shadow:
    0 0 0 1px rgba(255, 255, 255, .05),
    0 24px 60px rgba(0, 0, 0, .55);
}
.device-screen {
  border-radius: 22px;
  overflow: hidden;
  background: var(--bg);
  /* The glass edge. Without it the screen and the bezel are one flat
     shape and the whole thing stops looking like hardware. */
  box-shadow: 0 0 0 1px rgba(0, 0, 0, .8);
}
.device-screen img { display: block; width: 100%; height: auto; }

/* The camera. On a modern iPad it sits on the LONG edge, because that's
   the edge you hold up in a video call — Apple moved it, and putting it
   back on the short edge is the detail that makes a mockup look dated. */
.device-cam {
  position: absolute;
  top: 5px; left: 50%;
  transform: translateX(-50%);
  width: 6px; height: 6px;
  border-radius: 50%;
  background: #0b0c0d;
  box-shadow: 0 0 0 1px rgba(255, 255, 255, .07);
}
.device-ipad { max-width: 1040px; }

/* ---- the hero pair ----
   The tablet reads as the workspace; the phone in front of it, smaller
   and overlapping, reads as the same thing in somebody's hand. Ends
   aligned so they sit on one shelf rather than floating.

   Below 760px the tablet goes away entirely. It isn't hidden to save
   layout trouble — at that width its screen is about 260px across and
   the gear card in it becomes unreadable texture, which is worse than
   not showing it. And a visitor reading this ON a phone is better served
   by the phone shot anyway. */
.hero-devices {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  max-width: 1240px;
  margin: clamp(28px, 4vw, 52px) auto 0;
  padding: 0 var(--pad);
}
.hero-devices .device-ipad { flex: 0 1 880px; min-width: 0; margin: 0; }
.hero-devices .device-iphone {
  flex: 0 0 246px;
  margin: 0 0 0 -78px;
  position: relative;
  z-index: 2;
}
@media (max-width: 760px) {
  .hero-devices .device-ipad { display: none; }
  .hero-devices .device-iphone { flex: 0 0 250px; margin: 0; }
}
/* ONE DEVICE, ON ITS OWN.

   The rules above are written for the home page's pair, where the phone
   deliberately overlaps the iPad by 78px. A lone phone inherits that
   negative margin and sits visibly left of center — which nobody would
   describe as broken, and everybody would feel.

   The landing pages each lead with a single screen of the one thing
   that page is about, so they get this. Bigger, too: with nothing
   beside it, the phone is the picture rather than an inset. */
.hero-devices.solo .device-iphone { flex: 0 0 268px; margin: 0; }
.hero-devices.solo .device-ipad { flex: 0 1 980px; margin: 0; }
.device-iphone { padding: 10px; border-radius: 44px; max-width: 320px; }
.device-iphone .device-screen { border-radius: 36px; }
/* The island, not a notch. Same reasoning as the camera. */
.device-iphone .device-cam {
  top: 20px; width: 78px; height: 22px; border-radius: 980px;
}

@media (max-width: 620px) {
  .device { padding: 6px; border-radius: 22px; }
  .device-screen { border-radius: 16px; }
}

/* ---- the app, running inside it ----
   Values lifted from app/style.css rather than eyeballed, so the thing
   on the marketing page and the thing you install are the same product.
   Rows are pills with rounded ends; the cue number is the biggest thing
   on the row because it's what gets called out loud across a room. */
.appui { background: var(--bg); font-size: 13px; }

.appui-top {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 12px;
  border-bottom: 1px solid var(--accent);
  background: #0f1012;
}
.appui-avatar {
  width: 26px; height: 26px; border-radius: 7px;
  background: var(--accent); color: #fff;
  font-size: 11px; font-weight: 700;
  display: grid; place-items: center;
}
.appui-title { font-weight: 600; color: var(--text); font-size: 14px; }
.appui-face {
  width: 24px; height: 24px; border-radius: 50%;
  margin-left: auto;
  background: linear-gradient(140deg, #6b7280, #374151);
}

.appui-now { padding: 11px 14px 13px; }
.appui-now-main { display: flex; align-items: baseline; gap: 9px; margin: 0; font-size: 15px; }
.appui-now-main strong { color: var(--text); font-weight: 600; }
.appui-now-label {
  font-size: 10px; font-weight: 800; letter-spacing: .08em;
  text-transform: uppercase; color: var(--muted);
}
.appui-now-late { color: var(--accent); font-size: 12px; font-weight: 600; }
.appui-now-next { margin: 3px 0 0; font-size: 12px; color: var(--muted); }

.appui-transport { display: flex; gap: 8px; margin-top: 11px; }
.appui-btn {
  flex: 1;
  padding: 9px 0;
  text-align: center;
  border-radius: 10px;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text);
  font-size: 13px; font-weight: 600;
}
.appui-btn.is-off { color: var(--muted); }
.appui-btn.is-go { background: var(--accent); border-color: var(--accent); color: #fff; }

.appui-tabs {
  display: flex;
  border-top: 1px solid var(--border);
  background: #0f1012;
}
.appui-tabs span {
  flex: 1;
  padding: 9px 2px 10px;
  text-align: center;
  font-size: 10px;
  color: var(--muted);
}
.appui-tabs .on {
  color: var(--accent);
  background: var(--accent-soft);
  box-shadow: inset 0 2px 0 var(--accent);
}

/* ---- the run of show itself ---- */
.ros-scroll {
  overflow-x: auto;
  overflow-y: hidden;
  padding: 0 12px 12px;
  -webkit-overflow-scrolling: touch;
}
.ros-scroll:focus-visible { outline: 2px solid var(--accent); outline-offset: -3px; }

.ros-table {
  border-collapse: separate;
  border-spacing: 0 6px;
  width: max-content; min-width: 100%;
  font-size: 13px;
}
.ros-table th, .ros-table td { text-align: left; padding: 9px 13px; white-space: nowrap; }

.ros-table thead th {
  position: sticky; top: 0; z-index: 3;
  background: var(--bg);
  font-size: 10px; font-weight: 800; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted);
  border-bottom: 2px solid var(--border);
}
/* The department heading carries its color solidly; the cells only tint
   when they have something in them, so a column with three entries in
   forty cues reads as three marks rather than a wall. */
.ros-table thead th.d { border-bottom-width: 3px; border-bottom-color: rgb(var(--rc)); color: rgb(var(--rc)); }
.ros-col-red    { --rc: 220, 70, 70; }
.ros-col-blue   { --rc: 70, 130, 230; }
.ros-col-amber  { --rc: 225, 175, 45; }
.ros-col-green  { --rc: 70, 180, 110; }
.ros-col-purple { --rc: 160, 110, 220; }
.ros-col-teal   { --rc: 60, 180, 190; }
.ros-col-slate  { --rc: 130, 140, 155; }

/* Each row is one pill: straight edges between, rounded at the two ends,
   so it reads as a single object rather than a run of chips. */
.ros-table tbody td {
  background: var(--panel);
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  color: var(--muted);
}
.ros-table tbody tr > td:first-child {
  border-left: 1px solid var(--border);
  border-radius: 10px 0 0 10px;
}
.ros-table tbody tr > td:last-child {
  border-right: 1px solid var(--border);
  border-radius: 0 10px 10px 0;
}
.ros-table td.d.has { background-image: linear-gradient(rgba(var(--rc), .14), rgba(var(--rc), .14)); }

/* The pinned columns. Opaque on purpose — a translucent sticky cell
   shows the scrolling text sliding underneath it. */
.ros-table .c-time, .ros-table .c-cue, .ros-table .c-item { position: sticky; z-index: 2; }
.ros-table thead .c-time,
.ros-table thead .c-cue,
.ros-table thead .c-item { background: var(--bg); z-index: 4; }
.ros-table .c-time { left: 0;    width: 96px;  font-variant-numeric: tabular-nums; }
.ros-table .c-cue  { left: 96px; width: 78px; }
.ros-table .c-item { left: 174px; width: 220px; color: var(--text); font-weight: 600; }

.ros-table .c-time b { display: block; font-size: 14px; font-weight: 700; color: var(--text); }
.ros-table .c-time span { display: block; font-size: 10px; color: var(--muted); }
/* The cue number gets read off a screen across a room. */
.ros-table .c-cue b {
  font-size: 21px; font-weight: 700; line-height: 1.1;
  font-variant-numeric: tabular-nums; color: var(--text);
}
.ros-table .c-cue em {
  display: inline-block; margin-left: 6px; vertical-align: middle;
  font-style: normal; font-size: 9px; font-weight: 800; letter-spacing: .04em;
  text-transform: uppercase;
  padding: 2px 6px; border-radius: 6px;
  border: 1px solid var(--border); background: var(--panel2); color: var(--text);
}

/* The row the room is on. Ringed in amber — the one color in this app
   that already means "look here", and it survives whatever color a
   customer's brand turns the accent. */
/* Layered, not substituted. The obvious way to write this is
   `background-color: var(--accent-soft)` — which is what the app does —
   but accent-soft is rgba at 14%, and these cells are STICKY. A
   translucent pinned cell shows the departments sliding along behind it,
   and it only breaks on the one row a visitor is most likely to be
   looking at. So the tint goes on as an image over an opaque panel. */
.ros-table tr.is-current td {
  background-color: var(--panel);
  background-image: linear-gradient(var(--accent-soft), var(--accent-soft));
  border-top: 2px solid var(--warn);
  border-bottom: 2px solid var(--warn);
}
.ros-table tr.is-current > td:first-child { border-left: 2px solid var(--warn); }
.ros-table tr.is-current > td:last-child { border-right: 2px solid var(--warn); }
.ros-table tr.is-current td.d.has {
  background-image:
    linear-gradient(rgba(var(--rc), .22), rgba(var(--rc), .22)),
    linear-gradient(var(--accent-soft), var(--accent-soft));
}

/* On a phone, ONE column freezes: the cue number.

   Three frozen columns is 394px on a 390px screen, which leaves nothing
   for the departments. Two is 174px, which leaves 216px — about one
   department column, so you scroll sideways into a strip and still can't
   read the thing you scrolled to see.

   So the anchor is the cue number alone, which is what gets said out
   loud anyway: "standing by on 110" means something on its own in a way
   that "standing by on Opening number" doesn't. Time and item scroll
   away with everything else.

   Header and body are unpinned together on purpose. Freeing one and not
   the other slides the columns out of line with their own headings,
   which is worse than either arrangement. Same breakpoint the app
   uses. */
@media (max-width: 639px) {
  .ros-table .c-time { position: static; width: auto; min-width: 84px; }
  .ros-table .c-item { position: static; width: auto; min-width: 170px; }
  /* Left edge of the scroller, since nothing is pinned ahead of it now.
     The cell is opaque, so the time column slides underneath rather than
     through it. */
  .ros-table .c-cue { left: 0; }
}

/* ---- feature tiles ---- */
/* ---- the feature rail ----
   Horizontal, snapping, and entirely CSS. Replaces a grid that could
   never be the right shape: any fixed number of cards divides badly at
   some column count, so every content change turned into arithmetic
   about breakpoints. A rail is indifferent to how many cards there are.

   It runs full-bleed while the FIRST card still lines up with the page
   text, which is the detail that makes it look designed rather than
   overflowing. That's the padding-inline calculation below. */
.rail-wrap {
  position: relative;
  margin-top: 40px;
  /* Lets the progress bar, which is a sibling, read the rail's scroll
     position. Without this the named timeline is invisible to it. */
  timeline-scope: --rail;
}

.rail {
  display: flex;
  gap: 16px;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  align-items: center;
  -webkit-overflow-scrolling: touch;
  /* Align the first card's left edge with the headline above it, then
     let the rest run off the right of the screen. */
  padding: 6px max(var(--pad), calc((100vw - var(--max)) / 2)) 8px;
  scroll-padding-inline: max(var(--pad), calc((100vw - var(--max)) / 2));
  scroll-timeline: --rail x;
  /* The scrollbar is hidden because the peeking card and the progress
     bar already say "this scrolls", and a permanent gray trough under
     eight cards is louder than either. Keyboard access is preserved by
     tabindex on the element itself, so this doesn't strand anyone. */
  scrollbar-width: none;
}
.rail::-webkit-scrollbar { display: none; }
.rail:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 22px; }

.rail-card {
  flex: 0 0 min(78vw, 340px);
  scroll-snap-align: center;
  display: flex;
  flex-direction: column;
  /* Tall enough for the entire screenshot, because the card that's
     centered has to show the whole screen — a cropped one reads as an
     image that failed to load rather than as a design. The cards either
     side are shrunk by the scroll animation below, which is what makes
     the middle one read as "the one you're looking at".

     672, not 640. Measured in a browser: at 640 the card whose heading
     wraps to two lines — "One run of show, not seventeen" — pushed its
     screenshot 21px past the bottom edge and had it clipped, on the
     HOME page as well as the new ones. The comment above was already
     right about what should happen; the number was 21px short of
     delivering it. */
  height: 672px;
  padding: 26px 24px 0;
  border: 1px solid var(--border);
  border-radius: 20px;
  overflow: hidden;
  /* The department color, at a whisper. Enough that a row of cards reads
     as a set of different things rather than one thing repeated. */
  background:
    linear-gradient(160deg, rgba(var(--rc), .18), rgba(var(--rc), .03) 44%, transparent 68%),
    var(--panel);
}
.rail-kicker {
  margin: 0 0 10px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: rgb(var(--rc));
}
.rail-card h3 { font-size: 22px; margin: 0 0 8px; }
.rail-body { margin: 0; font-size: 14px; line-height: 1.5; color: var(--muted); }

/* The screenshot runs off the bottom of the card.

   `margin-top: auto` drops it to the floor, `flex: 0 0 auto` stops the
   flex layout squashing it to fit, and the card's own overflow does the
   cutting. A screenshot that ends inside the card looks like a thumbnail
   of a phone; one that runs off the edge looks like the screen carries
   on past it, which is the whole trick. */
/* A CARD THAT IS ALSO A DOOR.

   On the home page the rail is a list of things the product does and the
   cards go nowhere, which is right — there was nowhere to go. The
   landing pages each end with a rail pointing at the other three, so
   somebody who arrived from a search for one thing can see the next one
   rather than read about it.

   color: inherit and no underline, because the whole card is the target
   and a blue underlined heading inside a bordered box reads as a broken
   style rather than as a link. The border lighting up on hover is what
   says it can be pressed. */
a.rail-card { text-decoration: none; color: inherit; }
a.rail-card:hover,
a.rail-card:focus-visible { border-color: var(--accent); }

.rail-shot {
  flex: 0 0 auto;
  margin: auto auto 0;
  width: 84%;
  max-width: 236px;
  height: auto;
  display: block;
  border: 1px solid var(--border);
  border-radius: 14px;
  /* Lifts it off the card so it reads as a layer rather than a patch. */
  box-shadow: 0 -12px 34px rgba(0, 0, 0, .5);
}

/* ---- the progress bar ----
   Apple puts a pill and a row of dots under theirs. Dots need to know
   which card you're on, which normally means JavaScript — so this reads
   the rail's own scroll position through a named scroll timeline
   instead, and stays a piece of CSS.

   Wrapped in @supports because that's recent. Where it isn't available
   the track simply doesn't appear, and nothing else changes: the rail
   still scrolls, and the peeking card still says so. */
.rail-bar { display: none; }
@supports (animation-timeline: scroll()) and (timeline-scope: --x) {
  .rail-bar {
    display: block;
    width: min(200px, 40vw);
    height: 4px;
    margin: 22px auto 0;
    border-radius: 980px;
    background: var(--border);
    overflow: hidden;
  }
  .rail-bar i {
    display: block;
    height: 100%;
    width: 40%;
    border-radius: inherit;
    background: var(--text);
    animation: railslide linear both;
    animation-timeline: --rail;
  }
  /* Travels the length of the track: at the start it sits left, at the
     end it sits right, and it tracks your thumb the whole way. */
  @keyframes railslide {
    from { transform: translateX(0); }
    to   { transform: translateX(150%); }
  }
}

@media (prefers-reduced-motion: reduce) { .rail { scroll-behavior: auto; } }

/* ---- the card in the middle is the one you're looking at ----
   Normally "which card is centered" is a JavaScript question: listen to
   scroll, measure each card, add a class. This asks it in CSS instead.

   `view(x)` gives each card its own timeline describing that card's
   progress across the rail's scrollport. Cover 0% is the moment it
   starts entering from the right, cover 100% is the moment it finishes
   leaving on the left — so 50% is exactly when it's centered. Put the
   peak of the keyframes at 50% and the card grows and brightens as it
   arrives, then recedes as it goes. It tracks a thumb continuously
   rather than snapping between states, because the scroll position IS
   the clock.

   Transform and opacity only. Those are the two properties a browser can
   animate without doing layout again, which matters when the animation
   runs on every frame of a scroll. Growing the card's HEIGHT to reveal
   the screenshot would look the same and stutter, so instead every card
   is already tall enough and the others are shrunk.

   Wrapped in @supports: where this isn't available every card simply
   renders at full size, which is a perfectly good rail. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .rail-card {
      animation: railpop linear both;
      animation-timeline: view(x);
      animation-range: cover 0% cover 100%;
      will-change: transform, opacity;
    }
    @keyframes railpop {
      0%, 100% { transform: scale(.88); opacity: .4; }
      44%, 56% { transform: scale(1);   opacity: 1; }
    }
  }
}

/* ---- pricing ---- */
.plans {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(290px, 1fr));
  gap: 18px;
  margin: 48px 0 24px;
}
.plan {
  border: 1px solid var(--border);
  border-radius: 20px;
  padding: 34px 30px;
  background: var(--panel);
  display: flex;
  flex-direction: column;
  text-align: left;
}
.plan-best { border-color: var(--accent); }
.plan .who { color: var(--muted); font-size: 16px; margin: 0 0 24px; }
.price { font-size: 52px; font-weight: 600; letter-spacing: -.03em; line-height: 1; margin: 0; }
.price span { font-size: 17px; font-weight: 400; color: var(--muted); letter-spacing: 0; }
.price-year { color: var(--muted); font-size: 16px; margin: 12px 0 26px; }
.price-year b { color: var(--text); font-weight: 600; }
.plan ul { list-style: none; margin: 0 0 30px; padding: 0; }
.plan li { padding: 8px 0 8px 28px; position: relative; font-size: 16px; color: var(--muted); }
.plan li::before {
  content: "";
  position: absolute;
  left: 4px; top: 16px;
  width: 12px; height: 6px;
  border-left: 2px solid var(--ok);
  border-bottom: 2px solid var(--ok);
  transform: rotate(-45deg);
}
.plan .btn { margin-top: auto; text-align: center; }
/* The button is a form now, not a link — each plan posts its own choice
   to checkout without a line of JavaScript. The form has to inherit the
   push-to-the-bottom that used to belong to the button, or the two plan
   cards stop lining up whenever their feature lists differ in length. */
.plan-go { margin-top: auto; display: flex; }
.plan-go .btn { width: 100%; margin-top: 0; }
.plan-note { color: var(--muted); font-size: 15px; margin: 0 auto; max-width: 60ch; }

/* ---- checkout ----
   Stripe's form mounts inside .pay-wrap. Everything around it is ours;
   the form itself is an iframe Stripe controls, so it takes its colors
   from the Branding settings in their dashboard rather than from here.
   Nothing in this block can reach inside it, and trying would break the
   isolation that keeps card numbers off this domain. */
.pay-title { font-size: clamp(32px, 5vw, 56px); }
.pay-wrap { max-width: 720px; margin-top: 28px; }
#checkout { min-height: 420px; }

/* The three facts that used to be Stripe's custom_text. Managed Payments
   refuses that parameter, which turned out to be a favour: on our own
   page these get our type and our spacing instead of three grey lines
   bolted to a form we can't reach. */
.pay-facts {
  list-style: none;
  margin: 0 0 26px;
  padding: 18px 20px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--panel);
}
.pay-facts li {
  position: relative;
  padding: 5px 0 5px 26px;
  color: var(--muted);
  font-size: 15px;
  line-height: 1.5;
}
.pay-facts b { color: var(--text); font-weight: 600; }
.pay-facts li::before {
  content: "";
  position: absolute;
  left: 3px; top: 13px;
  width: 11px; height: 6px;
  border-left: 2px solid var(--ok);
  border-bottom: 2px solid var(--ok);
  transform: rotate(-45deg);
}

/* Said out loud rather than left as an empty rectangle. A checkout that
   fails silently is one where somebody waits, decides the company is
   broken, and leaves — and they were holding a card when they decided
   it. */
.pay-problem {
  margin: 0;
  padding: 18px 20px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--warn);
  border-radius: 12px;
  background: var(--panel);
  color: var(--text);
  font-size: 16px;
  line-height: 1.55;
}
.pay-problem[hidden] { display: none; }

/* ---- signup ---- */
.signup { max-width: 640px; margin: 36px auto 0; }
/* justify-content matters more than it looks. The two fields flex to
   fill the row, so on a wide screen everything sits on one line and the
   button is wherever the fields leave it. The moment it wraps — which is
   most phones — the button drops onto a line of its own and, without
   this, sits hard against the left edge under two full-width fields.
   That's the last control on the page. */
.signup form { display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; }
.signup form .btn { flex: 0 0 auto; }
.signup input {
  flex: 1 1 220px;
  min-width: 0;
  padding: 15px 18px;
  border-radius: 980px;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text);
  font: inherit;
  font-size: 16px;
}
.signup input::placeholder { color: var(--muted); }
.signup input:focus { outline: 2px solid var(--accent); outline-offset: 1px; }
.form-note { color: var(--muted); font-size: 15px; margin: 18px 0 0; }

/* ---- footer ---- */
footer {
  border-top: 1px solid var(--border);
  padding: 40px 0 60px;
  color: var(--muted);
  font-size: 14px;
}
footer nav { display: flex; flex-wrap: wrap; gap: 20px; margin-bottom: 14px; }
footer a { color: var(--muted); }
footer a:hover { color: var(--text); text-decoration: underline; }

/* ---- the reveal ----
   Progressive enhancement, and only where the browser can do it without
   a script. Everything is fully visible without this rule; it only ever
   adds a fade as a band comes up. Off entirely for anyone who asked for
   less motion. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .reveal {
      animation: rise linear both;
      animation-timeline: view();
      animation-range: entry 5% cover 26%;
    }
    @keyframes rise {
      from { opacity: 0; transform: translateY(26px); }
      to   { opacity: 1; transform: none; }
    }

    /* The screenshots settle into place as you scroll.
       ------------------------------------------------
       This is SCRUBBED, not triggered: the animation's clock is the
       scroll position, so the image tracks the wheel in both directions
       instead of playing once and stopping. Scrolling back up runs it
       backwards. That reversibility is the whole reason it reads as
       expensive rather than as a page element arriving late.

       It starts small and round and ends full size with the corners the
       band already gives it, which is why border-radius is only named in
       the `from` — the `to` end inherits whatever the element declares,
       so this one keyframe works for a square screenshot and for the
       rounded run-of-show frame without knowing which is which.

       The hero is excluded on purpose. It's above the fold, so it has
       already "entered" before anyone scrolls; animating it means the
       first thing a visitor sees is a picture finishing a move it
       started without them. It just sits there, sharp, immediately. */
    .band-line .bleed img,
    .band-line .shot-placeholder,
    .band-line .device {
      animation: settle linear both;
      animation-timeline: view();
      animation-range: entry 8% cover 42%;
      transform-origin: 50% 100%;
      will-change: transform;
    }
    @keyframes settle {
      from { opacity: .5; transform: scale(.9) translateY(34px); border-radius: 28px; }
      to   { opacity: 1; transform: none; }
    }
  }
}

@media (max-width: 620px) {
  .top nav a.nav-hide { display: none; }
  .band { padding: 56px 0; }
}

/* The email step on checkout.

   One field and a button, replaced by Stripe's form once it loads. It
   exists because whether somebody gets a trial has to be decided before
   the session is created, and Stripe won't say who they are until after
   its own form is filled. */
.pay-email { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin: 0 0 18px; }
.pay-email label {
  flex-basis: 100%; font-size: 14px; font-weight: 600; color: var(--text);
}
.pay-email input {
  flex: 1; min-width: 220px; padding: 12px 14px; font-size: 16px;
  border: 1px solid var(--border); border-radius: 10px; background: var(--panel); color: var(--text);
}
.pay-email .btn { flex-shrink: 0; }
.pay-email-note { flex-basis: 100%; margin: 0; font-size: 13px; color: var(--warn); }
.pay-email[hidden] { display: none; }

/* ---- /how-it-works: the shape of the app ---------------------------

   The diagram and the three tab bars. Scoped to this page: nothing
   else on the site draws a hierarchy, and a generic name here would
   be a class somebody reuses for something that isn't one. */
/* THE DIAGRAM IS AS WIDE AS THE COPY.

   It used to carry width="700" height="300" in the markup, so it drew
   at 700px inside a column that is now 1120 and sat there looking like
   a thumbnail of itself. The viewBox is the drawing; the width
   attribute was only ever a default size for it.

   width="100%" in the markup and height:auto here, so it scales with
   the column and the type inside it scales with the picture — at 1120
   the 15px label in the viewBox renders at about 24. No overflow
   scroller any more either: there is no fixed frame left to overflow,
   and a scrollbar under a diagram that fits is a control that does
   nothing. */
.hiw-map {
  margin: 26px 0 30px;
  padding: 10px 0;
}
.hiw-map svg { display: block; width: 100%; height: auto; }

/* ONE PER ROW, NOT THREE ACROSS.

   Three columns was right while these were labels nobody pressed: the
   bars sat side by side and the reader compared them at a glance. The
   moment the tabs became pressable, that layout stopped working, and
   the arithmetic says so without needing a screenshot — a third of the
   column is about 243px, which is 27px a tab across eight tabs, and
   the word "Dashboard" at 10px is nearer 49px. Every long label would
   have been squeezed into an unreadable sliver or pushed out of its
   bar.

   Full width gives about 91px a tab, which fits the longest label with
   room to spare, and stacking them keeps the comparison anyway because
   they sit directly above one another. */
.hiw-bars {
  display: grid;
  gap: 14px;
  margin: 22px 0 20px;
}

/* Below about 490px even the full width cannot hold eight labels, so
   the bar scrolls sideways rather than crushing them. Sized from the
   same arithmetic: 8 tabs x ~52px of label plus padding. */
@media (max-width: 560px) {
  .hiw-bar .appui-tabs {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .hiw-bar .appui-tabs::-webkit-scrollbar { display: none; }
  .hiw-bar .appui-tabs label { flex: 0 0 auto; padding-left: 11px; padding-right: 11px; }
}

.hiw-bar {
  border: 1px solid var(--border);
  border-radius: var(--rc);
  background: var(--panel);
  padding: 14px 14px 12px;
}
.hiw-bar-solo { max-width: 460px; margin: 22px 0 20px; }

.hiw-bar-label {
  margin: 0 0 10px;
  font-size: 11px; font-weight: 800; letter-spacing: .08em;
  text-transform: uppercase; color: var(--accent);
}
.hiw-bar-note {
  margin: 11px 0 0;
  font-size: 13px; line-height: 1.5; color: var(--muted);
}

/* The bar itself is .appui-tabs, borrowed from the landing page so
   there is ONE mock of the app's tab bar on this site rather than two
   that drift apart. It expects a dark plate under it. */
.hiw-bar .appui {
  border: 1px solid var(--border);
  border-radius: 10px;
  overflow: hidden;
}
.hiw-bar .appui-tabs { border-top: 0; }

/* A tab a read-only account does NOT get. Struck through rather than
   hidden, because the point of that bar is the comparison — a reader
   has to see what is missing, and four absent tabs look like a
   different app rather than the same one narrowed. */
.hiw-off {
  text-decoration: line-through;
  opacity: .45;
}


/* ---- /how-it-works: the device you can press ----------------------

   TJ: "make it like a device that you can chose the different tabs on
   and when you select a tab, instead of content, it shows the
   description of what the tab does. Above the device window, show 3
   tabs that will change the device tabs from HQ, project, and show."

   Two radio groups, no JavaScript. One picks the level and swaps which
   device is on screen; one per device picks the tab and swaps what the
   screen says. The browser supplies arrow-key movement and a real
   focus ring for both, and it all works with scripts blocked — which
   matters most on the one page whose whole job is explaining the
   product to somebody who has never seen it. */

/* Off-screen but still focusable. NOT display:none and NOT
   visibility:hidden: either takes the input out of the tab order, and
   then every control here is unreachable by keyboard while looking
   perfect to a mouse. */
.hiw-radio {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---- the level switcher, above the device ---- */
.hiw-levels {
  display: flex;
  gap: 6px;
  justify-content: center;
  margin: 30px auto 0;
  padding: 6px;
  max-width: 660px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--panel);
}
.hiw-level {
  flex: 1;
  padding: 13px 14px;
  border-radius: 999px;
  text-align: center;
  font-size: 16.5px; font-weight: 600;
  color: var(--muted);
  cursor: pointer;
  -webkit-user-select: none;
  user-select: none;
  white-space: nowrap;
}
@media (hover: hover) { .hiw-level:hover { color: var(--text); } }

.hiw-tour:has(#hiw-lvl-hq:checked) .hiw-level[for="hiw-lvl-hq"],
.hiw-tour:has(#hiw-lvl-project:checked) .hiw-level[for="hiw-lvl-project"],
.hiw-tour:has(#hiw-lvl-show:checked) .hiw-level[for="hiw-lvl-show"] {
  background: var(--accent);
  color: #fff;
}
.hiw-tour:has(#hiw-lvl-hq:focus-visible) .hiw-level[for="hiw-lvl-hq"],
.hiw-tour:has(#hiw-lvl-project:focus-visible) .hiw-level[for="hiw-lvl-project"],
.hiw-tour:has(#hiw-lvl-show:focus-visible) .hiw-level[for="hiw-lvl-show"] {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* ---- the three devices: one showing, two not ---- */
.hiw-stage { display: none; }
.hiw-tour:has(#hiw-lvl-hq:checked) .hiw-stage-hq,
.hiw-tour:has(#hiw-lvl-project:checked) .hiw-stage-project,
.hiw-tour:has(#hiw-lvl-show:checked) .hiw-stage-show { display: block; }

/* THE DEMO IS THE SECTION NOW, so it is sized like one.

   TJ: "the next section below this should be the tabs demo and it
   should definitely be bigger than it currently is."

   660px made it an illustration sitting beside an argument. This is
   the argument: it is the only place on the site where somebody can
   press a tab and find out what the screen is for. Widened past the
   prose column it sits in, which is what tells a reader to stop and
   use it rather than scroll past. */
.hiw-device {
  /* THE FULL COLUMN, and no wider.

     The first attempt at "bigger" set max-width: 880px and then tried
     to break out of the prose column with a negative inline margin —
     while also setting margin-inline: auto, which cancels exactly that
     and left the two rules arguing. A child cannot exceed its parent by
     max-width alone anyway, so the honest ceiling is the column: .narrow
     is 860px less 2 x --pad, about 812. That is a quarter wider than the
     660 it was, with no breakout trick that could put a horizontal
     scrollbar on a phone. */
  max-width: 100%;
  margin: 26px auto 6px;
}
.hiw-device .appui { font-size: 17px; }

/* The screen. Fixed height so switching tabs cannot shunt the page
   under the reader's cursor — worth the empty space under the short
   entries, because a control that moves the page while you use it
   feels broken even when it is right. */
/* Sized to FILL the frame now that the frame is the full column.
   At 660px wide the old 16px body and a 62ch cap used less than half
   the device and read as a large empty box with a caption in it. */
.hiw-screen { padding: 38px 46px 40px; min-height: 320px; }
.hiw-panel { display: none; }
.hiw-radio:checked + .hiw-panel { display: block; }

.hiw-panel-name { margin: 0; font-size: 40px; line-height: 1.14; letter-spacing: -.02em; }
.hiw-panel-where {
  margin: 8px 0 18px;
  font-size: 13px; font-weight: 800; letter-spacing: .08em;
  text-transform: uppercase; color: var(--accent);
}
/* 74ch at 21px is about 715px of the ~1030 the screen offers, which
   fills it without pushing the measure past reading width. */
.hiw-panel p:last-child { margin: 0; color: var(--muted); line-height: 1.58; font-size: 21px; max-width: 74ch; }

/* ---- the tab bar inside the device ---- */
.hiw-device .appui-tabs label {
  flex: 1;
  padding: 15px 4px 16px;
  text-align: center;
  font-size: 14px;
  color: var(--muted);
  cursor: pointer;
  -webkit-user-select: none;
  user-select: none;
}
@media (hover: hover) {
  .hiw-device .appui-tabs label:hover { color: var(--text); background: rgba(255,255,255,.04); }
}

.hiw-stage:has(#hiw-hq-dashboard:checked) .hiw-tab[for="hiw-hq-dashboard"],
.hiw-stage:has(#hiw-hq-team:checked) .hiw-tab[for="hiw-hq-team"],
.hiw-stage:has(#hiw-hq-shows:checked) .hiw-tab[for="hiw-hq-shows"],
.hiw-stage:has(#hiw-hq-calendar:checked) .hiw-tab[for="hiw-hq-calendar"],
.hiw-stage:has(#hiw-hq-task:checked) .hiw-tab[for="hiw-hq-task"],
.hiw-stage:has(#hiw-hq-chat:checked) .hiw-tab[for="hiw-hq-chat"],
.hiw-stage:has(#hiw-hq-admin:checked) .hiw-tab[for="hiw-hq-admin"],
.hiw-stage:has(#hiw-project-dashboard:checked) .hiw-tab[for="hiw-project-dashboard"],
.hiw-stage:has(#hiw-project-team:checked) .hiw-tab[for="hiw-project-team"],
.hiw-stage:has(#hiw-project-task:checked) .hiw-tab[for="hiw-project-task"],
.hiw-stage:has(#hiw-project-schedule:checked) .hiw-tab[for="hiw-project-schedule"],
.hiw-stage:has(#hiw-project-docs:checked) .hiw-tab[for="hiw-project-docs"],
.hiw-stage:has(#hiw-project-chat:checked) .hiw-tab[for="hiw-project-chat"],
.hiw-stage:has(#hiw-project-admin:checked) .hiw-tab[for="hiw-project-admin"],
.hiw-stage:has(#hiw-show-plan:checked) .hiw-tab[for="hiw-show-plan"],
.hiw-stage:has(#hiw-show-devices:checked) .hiw-tab[for="hiw-show-devices"],
.hiw-stage:has(#hiw-show-task:checked) .hiw-tab[for="hiw-show-task"],
.hiw-stage:has(#hiw-show-schedule:checked) .hiw-tab[for="hiw-show-schedule"],
.hiw-stage:has(#hiw-show-ros:checked) .hiw-tab[for="hiw-show-ros"],
.hiw-stage:has(#hiw-show-docs:checked) .hiw-tab[for="hiw-show-docs"],
.hiw-stage:has(#hiw-show-chat:checked) .hiw-tab[for="hiw-show-chat"],
.hiw-stage:has(#hiw-show-admin:checked) .hiw-tab[for="hiw-show-admin"] {
  color: var(--accent);
  background: var(--accent-soft);
  box-shadow: inset 0 2px 0 var(--accent);
}
.hiw-stage:has(#hiw-hq-dashboard:focus-visible) .hiw-tab[for="hiw-hq-dashboard"],
.hiw-stage:has(#hiw-hq-team:focus-visible) .hiw-tab[for="hiw-hq-team"],
.hiw-stage:has(#hiw-hq-shows:focus-visible) .hiw-tab[for="hiw-hq-shows"],
.hiw-stage:has(#hiw-hq-calendar:focus-visible) .hiw-tab[for="hiw-hq-calendar"],
.hiw-stage:has(#hiw-hq-task:focus-visible) .hiw-tab[for="hiw-hq-task"],
.hiw-stage:has(#hiw-hq-chat:focus-visible) .hiw-tab[for="hiw-hq-chat"],
.hiw-stage:has(#hiw-hq-admin:focus-visible) .hiw-tab[for="hiw-hq-admin"],
.hiw-stage:has(#hiw-project-dashboard:focus-visible) .hiw-tab[for="hiw-project-dashboard"],
.hiw-stage:has(#hiw-project-team:focus-visible) .hiw-tab[for="hiw-project-team"],
.hiw-stage:has(#hiw-project-task:focus-visible) .hiw-tab[for="hiw-project-task"],
.hiw-stage:has(#hiw-project-schedule:focus-visible) .hiw-tab[for="hiw-project-schedule"],
.hiw-stage:has(#hiw-project-docs:focus-visible) .hiw-tab[for="hiw-project-docs"],
.hiw-stage:has(#hiw-project-chat:focus-visible) .hiw-tab[for="hiw-project-chat"],
.hiw-stage:has(#hiw-project-admin:focus-visible) .hiw-tab[for="hiw-project-admin"],
.hiw-stage:has(#hiw-show-plan:focus-visible) .hiw-tab[for="hiw-show-plan"],
.hiw-stage:has(#hiw-show-devices:focus-visible) .hiw-tab[for="hiw-show-devices"],
.hiw-stage:has(#hiw-show-task:focus-visible) .hiw-tab[for="hiw-show-task"],
.hiw-stage:has(#hiw-show-schedule:focus-visible) .hiw-tab[for="hiw-show-schedule"],
.hiw-stage:has(#hiw-show-ros:focus-visible) .hiw-tab[for="hiw-show-ros"],
.hiw-stage:has(#hiw-show-docs:focus-visible) .hiw-tab[for="hiw-show-docs"],
.hiw-stage:has(#hiw-show-chat:focus-visible) .hiw-tab[for="hiw-show-chat"],
.hiw-stage:has(#hiw-show-admin:focus-visible) .hiw-tab[for="hiw-show-admin"] {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

/* Eight tabs will not fit across a phone at a readable size, so the
   bar scrolls sideways there rather than crushing the long labels.
   "Dashboard" at 10px is about 49px wide; below roughly 490px of
   screen there is no room for eight of those. */
@media (max-width: 560px) {
  .hiw-device .appui-tabs {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .hiw-device .appui-tabs::-webkit-scrollbar { display: none; }
  .hiw-device .appui-tabs label { flex: 0 0 auto; padding-left: 12px; padding-right: 12px; }
  .hiw-levels { max-width: none; }
  .hiw-level { font-size: 13px; padding: 10px 4px; }
  /* The sizes above are for a full-width device on a desktop column.
     On a phone the frame is ~340px, so they have to come back down or
     the title alone takes three lines and the body runs off the
     bottom. The measure cap does nothing here — the screen is narrower
     than 74 characters at any size — so only the type matters. */
  .hiw-screen { min-height: 330px; padding: 20px 20px 22px; }
  .hiw-panel-name { font-size: 26px; }
  .hiw-panel-where { margin: 6px 0 12px; font-size: 11px; }
  .hiw-panel p:last-child { font-size: 17px; line-height: 1.55; }
}

@media (prefers-reduced-motion: no-preference) {
  .hiw-radio:checked + .hiw-panel { animation: hiw-fade .18s ease-out; }
  @keyframes hiw-fade { from { opacity: 0; } to { opacity: 1; } }
}

/* The read-only bar further down the page is NOT pressable. It makes a
   point about access rather than about what a screen does, so it has
   nothing to point at and keeps the plain span styling. */
.hiw-bar {
  border: 1px solid var(--border);
  border-radius: var(--rc);
  background: var(--panel);
  padding: 14px 14px 12px;
}
.hiw-bar-solo { max-width: 460px; margin: 22px 0 20px; }
.hiw-bar-label {
  margin: 0 0 10px;
  font-size: 11px; font-weight: 800; letter-spacing: .08em;
  text-transform: uppercase; color: var(--accent);
}
.hiw-bar-note { margin: 11px 0 0; font-size: 13px; line-height: 1.5; color: var(--muted); }
.hiw-bar .appui { border: 1px solid var(--border); border-radius: 10px; overflow: hidden; }
.hiw-bar .appui-tabs { border-top: 0; }
.hiw-off { text-decoration: line-through; opacity: .45; }

/* The three words, on their own line under the claim. Sized in `em`
   rather than px so it tracks the h1's own clamp() at every width —
   pinning it would make it larger than the line above it on a phone,
   where the h1 has already scaled down to 40px. */
.hiw-terms {
  display: block;
  font-size: .72em;
  letter-spacing: -.02em;
  color: var(--muted);
  margin-top: .12em;
}

/* ---- /how-it-works: the access table ------------------------------

   TJ: "Build a table that shows the difference in access from company
   employee to freelancer to local labor."

   A table rather than three bars of tabs, because the question is a
   comparison: what a reader wants is to run a finger down the Local
   labor column and see where it says No. Three separate pictures make
   them hold two in their head to compare the third. */
.hiw-access-wrap {
  margin: 26px 0 30px;
  overflow-x: auto;              /* four columns need a floor on a phone */
  -webkit-overflow-scrolling: touch;
}
.hiw-access {
  width: 100%;
  min-width: 560px;
  border-collapse: collapse;
  font-size: 17px;
  text-align: left;
}
.hiw-access th,
.hiw-access td {
  padding: 13px 16px;
  border-bottom: 1px solid var(--border);
  vertical-align: baseline;
}
.hiw-access thead th {
  font-size: 13px; font-weight: 800; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted);
  border-bottom-color: var(--accent);
  white-space: nowrap;
}
/* The row label. Left column, and it carries the weight so the eye can
   find a row before reading across it. */
.hiw-access tbody th {
  font-weight: 600; color: var(--text); white-space: nowrap;
}
.hiw-access td { color: var(--body); }

/* Yes and No get colour, because the whole point of the table is the
   shape the Nos make down the right-hand column. Colour AND the word:
   a green dot on its own is unreadable to about one man in twelve, and
   this trade is mostly men. */
.hiw-access .yes { color: var(--ok); font-weight: 600; }
.hiw-access .no  { color: var(--warn); font-weight: 600; }

.hiw-access tbody tr:last-child th,
.hiw-access tbody tr:last-child td { border-bottom: 0; }

@media (max-width: 560px) {
  .hiw-access { font-size: 15px; }
  .hiw-access th, .hiw-access td { padding: 11px 12px; }
}

/* A qualified yes — "Read only", "Limited". Its own colour, because
   the table now has THREE states and collapsing this one into yes or
   no is the difference between "they can open the gear list" and
   "they can change it". */
.hiw-access .lim { color: var(--accent); font-weight: 600; }
