{
  "//": "Spec 034, FR-001. What makes the wardrobe installable. Comments are `//`-prefixed members, the shape `tsconfig.json` in this repository already uses — the Web App Manifest processing algorithm ignores members it does not know, so they cost nothing and this file is otherwise unexplainable.",

  "//static": "Static under `public/`, not `app/views/pwa/manifest.json.erb` rendered through a route. The generated ERB version was never reachable because the routes for it are commented out at the foot of `config/routes.rb`, which is why nothing has been installable. A dynamic manifest would earn its keep if any of these values depended on the request — none does — and a static file needs no route, no controller, no `skip_before_action :authenticate!` and no entry in `every_page_is_protected_spec.rb`'s list of deliberately public pages.",

  "id": "/",
  "//id": "Pinned rather than left to default to `start_url`. The `id` is what the browser uses to decide whether an install is *this* app or a second copy of it, and if it is absent it falls back to the start URL — so moving the start URL later would silently orphan every existing installation. `/` will never move.",

  "name": "Vitrine",
  "short_name": "Vitrine",

  "description": "Your wardrobe, what you wear, and the people you follow.",

  "start_url": "/wardrobe/feed",
  "//start_url": "The feed, which is what the wardrobe opens onto and what the plus button posts into. Somebody not signed in is sent to the sign-in page by `WardrobeController#authenticate!` exactly as they would be in a tab, which is User Story 1's first acceptance rather than a problem to route around.",

  "scope": "/",
  "//scope": "The whole origin, not `/wardrobe/`. Signing in is at `/sessions/new`, setting a password is at `/passwords/…`, and a scope of `/wardrobe/` would push the very first screen of the very first launch out of the installed window and into a browser tab.",

  "display": "standalone",
  "//display": "No address bar and no browser chrome — User Story 1, acceptance 1. `minimal-ui` keeps a reload button and `fullscreen` takes the status bar, which is a thing to do to a game and not to a wardrobe.",

  "background_color": "#ffffff",
  "theme_color": "#ffffff",
  "//colour": "Both are `--ground` from `app/frontend/styles/theme.css`, which is `#ffffff`, and `--w-paper` in `wardrobe.css` is defined as `var(--ground)` so the wardrobe's own ground is the same white. `background_color` is what the operating system paints during the launch, before a single byte of the page has rendered; getting it wrong is a coloured flash on every cold start. `theme_color` tints the system bars around the window. There is no dark palette in this product — `prefers-color-scheme` appears nowhere in `theme.css` — so there is deliberately no second colour here.",

  "orientation": "any",
  "//orientation": "Not locked. The wardrobe is drawn for a phone held upright and it would be easy to write `portrait` here, but locking orientation is a product decision about somebody else's device — it overrides a rotation lock a person set for themselves, and it is the wrong answer on a tablet. Left open until somebody decides otherwise on purpose.",

  "lang": "en",
  "dir": "ltr",

  "icons": [
    {
      "src": "/icon.svg",
      "type": "image/svg+xml",
      "sizes": "any",
      "purpose": "any"
    },
    {
      "src": "/icon.png",
      "type": "image/png",
      "sizes": "512x512",
      "purpose": "any"
    }
  ],
  "//icons": "**These are the Rails generator's placeholders — a red circle — and they are declared `any` rather than `maskable` on purpose.** A `maskable` icon promises that everything that matters sits inside the middle 80% of the canvas, because the platform crops the rest to whatever shape it likes; the generated circle is full-bleed, so declaring it maskable would be a claim that is simply false and Android would crop it to a smaller circle. The generated `manifest.json.erb` declared exactly that. Real artwork wants three entries: 192×192 and 512×512 `any`, plus a 512×512 `maskable` drawn with the safe zone respected."
}
