/*
 * The room.
 *
 * One photograph of a wooden room, and boards hung in it. That is the whole
 * idea now, and the shrinking is deliberate: this file used to assemble the
 * room out of three planes — a blurred shelf wall, a foreground counter lip and
 * a shelf plank under the goods — and on a long scroll they read as three
 * unrelated bands sliding past one another rather than as one place. A
 * photograph of a real room already contains its walls, its floor and its
 * perspective, and they already agree with each other.
 *
 * Every colour is a token; tokens.css is still the only file allowed a literal,
 * and spec/design/palette_spec.rb enforces it.
 *
 * ── The rule that still shapes this file ───────────────────────────────────
 *
 * THE BACKDROP IS A SIBLING OF THE CONTENT, NEVER AN ANCESTOR OF IT.
 *
 * A 3D transform, perspective or filter on any ancestor makes that ancestor the
 * containing block for position:fixed descendants, and breaks position:sticky
 * the same way. Two things depend on those:
 *
 *   .till-bar    (till.css)       "Take payment", pinned to the viewport so a
 *                                 ten-line bill cannot push it out of reach
 *   .connection  (components.css) the banner saying the till is offline
 *
 * spec/design/room_spec.rb fails if that stops being true.
 */

/* ── The backdrop ──────────────────────────────────────────────────────────── */

/*
 * Fixed and viewport-sized, so it needs one cover-sized image and never tiles —
 * which matters, because a photograph of wood cannot be mirror-tiled without
 * every grain line kaleidoscoping into a lozenge grid.
 *
 * `bottom` and not `center`: the source is a tall portrait frame and the floor
 * is the bottom third of it. Anchoring to the bottom keeps the floor on screen
 * on a wide desktop, where a centred crop would show wall and nothing to stand
 * on. The floor is what makes it a room rather than a wallpaper.
 *
 * The veil is the first layer, so it sits OVER the photograph. By day it screens
 * a warm amber in, which is not decoration: this room measures L 0.094 on its
 * back wall and the day plaque sits at L 0.085, so bare, a board on it is a dark
 * shape on a dark shape. The veil lifts the wall to about 0.26 and gives the
 * boards something to be seen against. At night it darkens instead, and the
 * boards become the lit thing in the room.
 */
.room {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    linear-gradient(var(--room-veil), var(--room-veil)),
    url("/assets/brand/room-900-6f01bc34.webp") center bottom / cover no-repeat;
  background-blend-mode: var(--room-blend);
  opacity: var(--wood-opacity);
}

/*
 * .gate paints its own opaque --color-canvas over the whole viewport, which hid
 * the room completely on the sign-in and PIN screens. Worth knowing why it only
 * bit there: body's background propagates to the canvas and therefore paints
 * BELOW a z-index:-1 backdrop, but any ordinary element's background paints
 * above it. So the gate stands down and lets the room show.
 */
.room-lit .gate {
  background: none;
}

/*
 * The shop's name sits on the room, not on a painted bar.
 *
 * .room-lit and not .room-full, which is a REVERSAL. This read ".room-full" and
 * the comment said the topbar keeps its terracotta on admin because "admin and
 * the till are tools, and a tool wants a chrome that stays put". Half of that
 * still holds and the half that does is the till — .room-plain, which keeps the
 * painted bar and its counter edge below.
 *
 * Admin was the wrong half. It stands in the same room as the storefront: the
 * SAME photograph, the same veil and the same opacity, because .room-ground has
 * no rules of its own in this file and never did — it is a marker meaning "no
 * plaques", nothing more. So a terracotta band across the top of it was a strip
 * of a different shop laid over the one behind it, and the boards below it —
 * the light switch, and now the menu — were already the room's.
 *
 * Everything the scrim below measures was measured against those same pixels,
 * so widening this selector moves no wood and changes no contrast.
 *
 * The words stay --color-text-inverse.
 */
.room-lit .topbar {
  position: relative;
  background: none;
}

/*
 * ...but "no bar" cannot mean "no contrast". Measured on the real page, the
 * brightest wood behind the shop's name reaches L 0.373 on a desktop and L 0.573
 * on a phone, which puts --color-text-inverse at 2.26:1 and 1.53:1. The comment
 * this replaces claimed the top of the frame was the dark ceiling; it is not,
 * because the room is anchored to its floor and a wide viewport crops the
 * ceiling away entirely.
 *
 * The scrim extends BELOW the bar and fades out, so there is no edge to read as
 * a bar and the planks stay visible through it.
 */
.room-lit .topbar::before {
  content: "";
  position: absolute;
  inset: 0 0 -2.5rem;
  background: linear-gradient(to bottom,
              var(--topbar-scrim) 0%,
              var(--topbar-scrim) 58%,
              transparent 100%);
  pointer-events: none;
}

/* Both are positioned, so DOM order decides: the bar's contents come after the
   ::before and paint over it. */
.room-lit .topbar__inner {
  position: relative;
}

/*
 * The till has no room, but it does stand on a counter. One plank edge under the
 * topbar, the thickness of a real counter's lip — enough that the till belongs
 * to the same shop as the storefront, and not enough to put anything behind a
 * total.
 */
.room-plain .topbar {
  border-block-end: 4px solid var(--counter-edge);
  box-shadow: inset 0 -5px 4px -4px rgb(0 0 0 / 35%);
}

/*
 * Goods on a shelf: a card in the room casts the room's shadow. Admin cards get
 * this and nothing else — see the plaques below for why they stop there.
 */
.room-lit .card,
.room-lit .good,
.room-lit .gate__card {
  box-shadow: var(--shadow-card), inset -1px 1px 0 var(--rim-light);
}

/* ── The plaques ───────────────────────────────────────────────────────────── */

/*
 * A card on the storefront is a board hung on the wall: dark oiled wood, pale
 * lettering, a lit top edge and a shadowed bottom one.
 *
 * .room-full and not .room-lit, deliberately. Admin is in the same room but its
 * cards hold tables of figures, and a table of figures on a dark board is worse
 * than one on paper. The till has no room at all.
 *
 * The page header and the storefront footer are in this list because otherwise
 * they are the only two blocks of words standing on bare wood, and they were
 * carrying a pale near-opaque "reading ground" to survive it — the one element
 * on the page that looked like a sticky note rather than a shop.
 */
.room-full :is(.card, .good, .gate__card, .page-header, .storefront-footer, .sheet:modal) {
  background:
    linear-gradient(var(--plaque-veil), var(--plaque-veil)),
    var(--plaque-image) center / cover no-repeat;
  border: 1px solid var(--plaque-edge-dark);
  border-radius: var(--radius-lg);
  color: var(--color-on-plaque);
  box-shadow:
    inset 0 1px 0 var(--plaque-edge-lit),
    inset 0 -3px 6px -3px var(--plaque-edge-dark),
    var(--shadow-card);
}

/* The two that were not cards need a card's padding to become boards. */
.room-full :is(.page-header, .storefront-footer) {
  padding: var(--space-4);
  margin-block-end: var(--space-4);
}

/*
 * The heading above a plaque's content, and every quiet line inside it.
 *
 * The last four are the INVERSE of the hazard documented further down. That one
 * is a component bringing a light BACKGROUND and inheriting its colour; this is
 * a component hard-setting a light-theme COLOUR and bringing no background.
 * --color-text-muted is a mid warm brown by day, and on a board it measures
 * under 2:1.
 * `.figure dt` is the hours on the front page, and it only escaped notice
 * because the shop has no hours entered yet and the list never renders.
 */
.room-full :is(.card, .good, .gate__card, .page-header, .storefront-footer, .sheet:modal)
  :is(.card__title, .muted, .figure dt, .timeline__step--todo, .field__hint),
.room-full :is(.card, .good, .gate__card) .table--stacked td[data-label]::before,
.room-full .gate__aside {
  color: var(--color-on-plaque-muted);
}

/*
 * A link on a board is a TAB RAISED OUT of it — the exact inverse of the light
 * switch's knob, which is a block pressed into it.
 *
 *   pressed in   a whole block, sunk    the switch's chosen position
 *   raised out   a whole block, proud   a link
 *
 * Compare .lightswitch__knob: it is `inset 0 2px 3px` dark along the top, where
 * the rim of the hole shadows what has gone into it, and a thin lit line along
 * the bottom where the floor of the socket catches the bulb. This is that read
 * backwards — lit along the TOP face, which is the face turned toward a bulb in
 * the top right, its own underside in shadow, and a shadow thrown down and left
 * onto the board it is standing on.
 *
 * The first attempt put this on the LETTERFORMS with text-shadow. Wrong effect:
 * the toggle's intrusion is a block, so the inverse has to be a block too. Glyph
 * relief is so subtle at reading size that it read as flat text next to a
 * plainly recessed knob.
 *
 * The tab is cut from the same board at the same veil, so the wood under a link
 * is the wood everywhere else and --color-link-on-plaque still measures the
 * 5.11:1 by day and 5.77:1 by night that spec/design/plaque_spec.rb checks. What
 * makes it a separate piece of wood is the lit edge and the cast shadow, not a
 * different colour — and `cover` on a small box lands on a different part of the
 * grain than the big board behind it, so the two do not line up, which is what
 * a real tab would do.
 *
 * No underline. A raised tab is a shape, not a colour, so it distinguishes a
 * link without one — the same way nobody underlines the words on a button.
 */
.room-full :is(.card, .good, .gate__card, .page-header, .storefront-footer, .sheet:modal)
  :is(a:not(.button), .counter__more) {
  display: inline-block;
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-sm);
  color: var(--color-link-on-plaque);
  text-decoration: none;
  background:
    linear-gradient(var(--plaque-veil), var(--plaque-veil)),
    var(--plaque-image) center / cover no-repeat;
  /*
   * Six layers, and each one is a face of the same block.
   *
   * The bulb is top-right, so the top and right bevels catch it and the bottom
   * and left are in their own shadow — inset offsets read backwards, so
   * `inset -1px 0 0` paints the RIGHT inner edge and `inset 1px 0 0` the left.
   * Bevelling all four rather than two is most of what makes it look like a
   * block rather than a lighter patch.
   *
   * Then TWO cast shadows, which is the trick that reads as height: a tight
   * dark one for where the tab nearly touches the board, and a wide soft one
   * for the light it blocks from everywhere else. One shadow says "offset";
   * two say "lifted".
   *
   * Note what is NOT here: the tab's face is not lightened, and that is a
   * measurement rather than a preference. Lifting the face more than about 14%
   * by day takes --color-link-on-plaque from 5.12:1 to 4.34:1 and fails, so a
   * visible face lift is simply not available. All of the contrast therefore
   * comes from --tab-bevel on the two lit edges and from the pair of cast
   * shadows, neither of which ever sits behind a glyph. The face stays exactly
   * the board's wood, and the words on it keep the 5.11:1 by day and 5.77:1 by
   * night that plaque_spec measures.
   */
  box-shadow:
    inset 0 1px 0 var(--tab-bevel),
    inset -1px 0 0 var(--tab-bevel),
    inset 0 -1px 0 var(--relief-shadow),
    inset 1px 0 0 var(--relief-shadow),
    -1px 2px 3px rgb(0 0 0 / 55%),
    -4px 8px 16px rgb(0 0 0 / 50%);
}

/*
 * Pressed, it goes in — and goes in as the knob does, with the same two inset
 * edges the switch uses. The tab drops onto the board and its cast shadow goes
 * with it.
 */
.room-full :is(.card, .good, .gate__card, .page-header, .storefront-footer, .sheet:modal)
  a:not(.button):active,
.room-full .counter:active .counter__more {
  box-shadow:
    inset 0 2px 3px var(--relief-shadow),
    inset 0 -1px 0 var(--relief-light);
}

/*
 * THE HAZARD OF A DARK SURFACE IN A LIGHT THEME.
 *
 * A component that paints its own light background but inherits its text colour
 * is invisible on a plaque: it keeps its cream surface and takes the board's
 * bone lettering. The delivery pills did exactly this, and only in daylight —
 * at night --color-surface-sunken is dark, so the inherited bone was right by
 * accident and the bug hid.
 *
 * The list is not guesswork: it is every selector in the stylesheets that sets a
 * --color-surface/-sunken/-canvas background and no colour of its own.
 * spec/design/plaque_spec.rb runs that same scan and fails if one appears that
 * is not named here.
 */
.room-full :is(.card, .good, .gate__card, .page-header, .storefront-footer, .sheet:modal)
  :is(.pill, .tile, .table, .choice, .disclosure__body, .line__section,
      .receipt-line, .stk-status, .order-alert__count) {
  color: var(--color-text);
}

/* ...and the quiet text inside those keeps the page's muted, not the board's. */
.room-full :is(.card, .good, .gate__card, .page-header, .storefront-footer, .sheet:modal)
  :is(.pill, .tile, .table, .choice, .disclosure__body) .muted {
  color: var(--color-text-muted);
}

/*
 * A quantity box and an add button on a plaque bring their own pale background
 * and their own colour, so they only need their borders redrawn: they were drawn
 * for paper and are now sitting on wood.
 */
.room-full .good :is(.add-measure__qty, .button--secondary) {
  border-color: var(--plaque-edge-dark);
}

/* ── Etching ───────────────────────────────────────────────────────────────── */

/*
 * A fact cut into the board: a phone number, an address, the opening hours. For
 * things that INFORM and do not link — a link has a job and should look like it
 * has one, so nothing interactive is ever etched.
 *
 * The depth is the two shadows, not a dimmer colour: see tokens.css. The letter
 * keeps a measured 5.18:1 by day and 5.86:1 by night, which
 * spec/design/plaque_spec.rb re-measures against the real board.
 */
.room-full .etched {
  color: var(--color-etched);
  text-shadow:
    1px -1px 1px var(--relief-shadow),
    -1px 1px 0 var(--relief-light);
}

/* A shop's number, painted big on the board the way it is on a shopfront. It is
   not a link: the button underneath it is, and a dialer shows the number back
   to you anyway. */
.room-full .etched--number {
  font-size: var(--text-2xl);
  font-weight: 700;
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
  margin-block-end: var(--space-3);
}

/* ── The front page ────────────────────────────────────────────────────────── */

/*
 * The basket stands in the room and the words hang beside it on a board. It
 * used to be a photograph of a DIFFERENT room with the words scrimmed on top,
 * which is why the page never quite settled: two rooms, one above the other.
 *
 * Side by side where there is room, stacked where there is not — the same rule
 * as everywhere else on a phone.
 */
.landing-hero {
  display: grid;
  gap: var(--space-4);
  align-items: center;
  margin-block-end: var(--space-5);
}

@media (min-width: 52rem) {
  .landing-hero {
    grid-template-columns: 1.05fr 1fr;
    gap: var(--space-6);
  }
}

/*
 * Cut out, on nothing, so the room shows around it. The drop shadow is what
 * stands it ON the floor instead of floating it in front — same direction as
 * every other shadow in the app.
 */
.landing-hero__basket {
  display: block;
  width: 100%;
  height: auto;
  filter: drop-shadow(-6px 14px 18px rgb(0 0 0 / 45%));
}

.landing-hero__words {
  margin: 0;
  text-align: center;
}

/*
 * Press-to-lift. A customer's tap should move the thing they touched, the way
 * taking something off a shelf does. Storefront only — the till is a tool and a
 * seller in a hurry does not need the merchandise moving.
 */
/*
 * The whole board is a link now, so it has to answer a pointer — and the edge
 * catching the light is the cue, deliberately, because a colour change is not
 * motion. Someone who has asked for reduced motion still gets an answer.
 */
.room-full :is(.counter, .device-plaque):hover {
  border-color: var(--plaque-edge-lit);
}

@media (prefers-reduced-motion: no-preference) {
  .room-lit :is(.landing-counters .card, .device-plaque) {
    transition: box-shadow 120ms ease-out, translate 120ms ease-out,
                border-color 120ms ease-out;
  }

  /* Lifted toward you under the pointer, pressed back down under a thumb. */
  .room-lit :is(.landing-counters .card, .device-plaque):hover {
    translate: 0 -2px;
  }

  .room-lit :is(.landing-counters .card, .device-plaque):active {
    translate: 0 1px;
    box-shadow: 0 1px 2px rgb(0 0 0 / 12%), -2px 3px 8px rgb(0 0 0 / 10%);
  }
}

/* ── When the room has to get out of the way ───────────────────────────────── */

/*
 * Motion is a new category in this app — there were no transitions, no
 * animations and no reduced-motion handling anywhere before the room. So it is
 * spelled out rather than assumed. These selectors match the ones that ADD the
 * motion character for character: `.room-lit .card` would score 0,2,0 against
 * the 0,3,0 that adds it, lose the cascade, and leave the transition running.
 */
@media (prefers-reduced-motion: reduce) {
  .room-lit :is(.landing-counters .card, .device-plaque) {
    transition: none;
  }

  .room-lit :is(.landing-counters .card, .device-plaque):hover,
  .room-lit :is(.landing-counters .card, .device-plaque):active {
    translate: none;
  }
}

/*
 * Someone who has asked for more contrast, or for less transparency, is telling
 * you they are struggling to read the screen. The photographs go first: the
 * tokens alone are already measured at AA in both lights, and the pictures are
 * the part that is decoration.
 */
@media (prefers-contrast: more), (prefers-reduced-transparency: reduce) {
  .room {
    background: var(--color-canvas);
  }

  .room-lit .topbar {
    background: var(--color-action);
  }

  .room-full :is(.card, .good, .gate__card, .page-header, .storefront-footer, .sheet:modal) {
    background: var(--color-surface);
    color: var(--color-text);
  }

  .room-full :is(.card, .good, .gate__card, .page-header, .storefront-footer, .sheet:modal)
    :is(.card__title, .muted),
  .room-full .gate__aside {
    color: var(--color-text-muted);
  }

  .room-full :is(.card, .good, .gate__card, .page-header, .storefront-footer, .sheet:modal)
    :is(a:not(.button), .counter__more) {
    padding: 0;
    color: var(--color-link);
    background: none;
    box-shadow: none;
    text-decoration: underline;
  }

  .room-full .gate__brand {
    color: var(--color-action);
  }

  .landing-hero__basket {
    filter: none;
  }

  /* Nothing is carved into a flat surface. */
  .room-full .etched {
    color: var(--color-text-muted);
    text-shadow: none;
  }
}

/* ── Print ─────────────────────────────────────────────────────────────────── */

@media print {
  .room { display: none; }

  /* Pale letters on dark wood is a photograph of a board, and a printer would
     lay down a page of ink to say so. On paper a card is a card. */
  .room-full :is(.card, .good, .gate__card, .page-header, .storefront-footer, .sheet:modal) {
    background: none;
    color: var(--color-text);
    box-shadow: none;
  }

  .room-full :is(.card, .good, .gate__card, .page-header, .storefront-footer, .sheet:modal)
    :is(a:not(.button), .muted, .card__title, .counter__more) {
    padding: 0;
    color: var(--color-text);
    background: none;
    box-shadow: none;
    text-shadow: none;
  }

  .landing-hero__basket { filter: none; }

  .room-full .etched {
    color: var(--color-text);
    text-shadow: none;
  }
}
