woolpack 0.2.0__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. {woolpack-0.2.0/src/woolpack.egg-info → woolpack-0.3.0}/PKG-INFO +1 -1
  2. {woolpack-0.2.0 → woolpack-0.3.0}/pyproject.toml +2 -2
  3. {woolpack-0.2.0 → woolpack-0.3.0}/src/woolpack/__init__.py +1 -1
  4. {woolpack-0.2.0 → woolpack-0.3.0}/src/woolpack/cli.py +1 -1
  5. {woolpack-0.2.0 → woolpack-0.3.0}/src/woolpack/lint.py +20 -2
  6. {woolpack-0.2.0 → woolpack-0.3.0}/src/woolpack/resources/style.css +228 -62
  7. {woolpack-0.2.0 → woolpack-0.3.0/src/woolpack.egg-info}/PKG-INFO +1 -1
  8. {woolpack-0.2.0 → woolpack-0.3.0}/LICENSE +0 -0
  9. {woolpack-0.2.0 → woolpack-0.3.0}/LICENSE-CC0 +0 -0
  10. {woolpack-0.2.0 → woolpack-0.3.0}/README.md +0 -0
  11. {woolpack-0.2.0 → woolpack-0.3.0}/setup.cfg +0 -0
  12. {woolpack-0.2.0 → woolpack-0.3.0}/src/woolpack/cards.py +0 -0
  13. {woolpack-0.2.0 → woolpack-0.3.0}/src/woolpack/contract.py +0 -0
  14. {woolpack-0.2.0 → woolpack-0.3.0}/src/woolpack/py.typed +0 -0
  15. {woolpack-0.2.0 → woolpack-0.3.0}/src/woolpack/render.py +0 -0
  16. {woolpack-0.2.0 → woolpack-0.3.0}/src/woolpack/resources/__init__.py +0 -0
  17. {woolpack-0.2.0 → woolpack-0.3.0}/src/woolpack/resources/pebble/pack.yaml +0 -0
  18. {woolpack-0.2.0 → woolpack-0.3.0}/src/woolpack/resources/pebble/phrases/pebble.yaml +0 -0
  19. {woolpack-0.2.0 → woolpack-0.3.0}/src/woolpack/resources/pebble/quirks/sunbather.yaml +0 -0
  20. {woolpack-0.2.0 → woolpack-0.3.0}/src/woolpack/resources/pebble/species/pebble.svg +0 -0
  21. {woolpack-0.2.0 → woolpack-0.3.0}/src/woolpack/resources/pebble/species/pebble.yaml +0 -0
  22. {woolpack-0.2.0 → woolpack-0.3.0}/src/woolpack/resources/pebble/voice.yaml +0 -0
  23. {woolpack-0.2.0 → woolpack-0.3.0}/src/woolpack/sanitize.py +0 -0
  24. {woolpack-0.2.0 → woolpack-0.3.0}/src/woolpack/scaffold.py +0 -0
  25. {woolpack-0.2.0 → woolpack-0.3.0}/src/woolpack/validation.py +0 -0
  26. {woolpack-0.2.0 → woolpack-0.3.0}/src/woolpack.egg-info/SOURCES.txt +0 -0
  27. {woolpack-0.2.0 → woolpack-0.3.0}/src/woolpack.egg-info/dependency_links.txt +0 -0
  28. {woolpack-0.2.0 → woolpack-0.3.0}/src/woolpack.egg-info/entry_points.txt +0 -0
  29. {woolpack-0.2.0 → woolpack-0.3.0}/src/woolpack.egg-info/requires.txt +0 -0
  30. {woolpack-0.2.0 → woolpack-0.3.0}/src/woolpack.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: woolpack
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: Standalone validation and authoring tools for Woolroom content packs.
5
5
  License-Expression: MIT AND CC0-1.0
6
6
  Project-URL: Homepage, https://www.minglongpan.com/projects/woolroom
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "woolpack"
3
- version = "0.2.0"
3
+ version = "0.3.0"
4
4
  description = "Standalone validation and authoring tools for Woolroom content packs."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -26,7 +26,7 @@ Repository = "https://github.com/minglong51/woolroom"
26
26
  woolpack = "woolpack.cli:main"
27
27
 
28
28
  [build-system]
29
- requires = ["setuptools>=77"]
29
+ requires = ["setuptools==84.0.0"]
30
30
  build-backend = "setuptools.build_meta"
31
31
 
32
32
  [tool.setuptools.packages.find]
@@ -13,7 +13,7 @@ from woolpack.validation import ValidatedPack, validate_pack
13
13
  try:
14
14
  __version__ = version("woolpack")
15
15
  except PackageNotFoundError:
16
- __version__ = "0.2.0"
16
+ __version__ = "0.3.0"
17
17
 
18
18
  __all__ = [
19
19
  "DEFAULT_ENVIRONMENT",
@@ -10,7 +10,7 @@ def _package_version() -> str:
10
10
  try:
11
11
  return version("woolpack")
12
12
  except PackageNotFoundError:
13
- return "0.2.0"
13
+ return "0.3.0"
14
14
 
15
15
 
16
16
  def main(argv: list[str] | None = None) -> int:
@@ -12,7 +12,8 @@ quality contract that the runtime gates deliberately do not:
12
12
  ERROR, not a suggestion), and the `.coat`/`.cream`/`.point` palette
13
13
  hooks the inline `--dog-*` coat vars land on. The ambient layers
14
14
  (`.brushstreak`/`.touchfibers`/`.paw-dream`/`.dog-contact`) are
15
- nice-to-have: missing is a WARN.
15
+ nice-to-have: missing is a WARN. `.breath` and `.squishg` are forbidden:
16
+ the host injects those animation wrappers around every figure.
16
17
  - sanitizer drops — the elements/attributes `sanitize_svg` WOULD strip
17
18
  from the raw figure, named non-destructively, so an author is not
18
19
  confused when the loaded figure is thinner than the file they drew.
@@ -65,6 +66,7 @@ RIG_STRUCTURE = {"tailg", "headg", "earg"}
65
66
  RIG_EYE_STATES = {"eyes-open", "eyes-happy", "eyes-side", "nap-eyes", "one-eye-eyes"}
66
67
  RIG_PALETTE = {"coat", "cream", "point"}
67
68
  RIG_EXTRAS = {"brushstreak", "touchfibers", "paw-dream", "dog-contact"}
69
+ HOST_OWNED_WRAPPERS = {"breath", "squishg"}
68
70
  EYE_SINGLETON = "dog-eyes"
69
71
 
70
72
  # The scene frame (wool.js pointer math) and the room's pettable rect
@@ -153,6 +155,22 @@ def _sanitize_drops(text: str) -> tuple[list[str], list[str]]:
153
155
  def _check_rig(report: LintReport, species_id: str, figure: str) -> None:
154
156
  classes, ids = _figure_handles(figure)
155
157
 
158
+ host_wrappers = sorted(HOST_OWNED_WRAPPERS & classes)
159
+ if host_wrappers:
160
+ report.add(
161
+ f"rig wrapper ownership [{species_id}]",
162
+ ERROR,
163
+ f"contains {' '.join(f'.{name}' for name in host_wrappers)} — the host injects "
164
+ ".breath and .squishg around the figure; declaring either inside the fragment "
165
+ "double-applies its animation",
166
+ )
167
+ else:
168
+ report.add(
169
+ f"rig wrapper ownership [{species_id}]",
170
+ PASS,
171
+ ".breath .squishg absent — the host owns both animation wrappers",
172
+ )
173
+
156
174
  missing = sorted(RIG_STRUCTURE - classes)
157
175
  singleton = ids.count(EYE_SINGLETON)
158
176
  problems = []
@@ -374,7 +392,7 @@ def _check_overlay(
374
392
  f"overlay sparsity [{species_id}]",
375
393
  WARN,
376
394
  "pins no body cells — the ambient fallback voice (the lines heard most) is "
377
- "entirely the base dog tables",
395
+ "entirely the builtin base tables",
378
396
  )
379
397
  else:
380
398
  report.add(
@@ -81,7 +81,7 @@ body {
81
81
  }
82
82
 
83
83
  .root {
84
- max-width: 940px;
84
+ max-width: 1180px;
85
85
  margin: 0 auto;
86
86
  padding: 28px 20px 24px;
87
87
  min-height: 100%;
@@ -330,11 +330,15 @@ button:disabled { opacity: 0.4; cursor: default; transform: none; }
330
330
 
331
331
  /* ---------- scene shell ---------- */
332
332
  .scene { display: flex; flex-direction: column; gap: 14px; }
333
- .scene-header { display: flex; align-items: baseline; justify-content: space-between; gap: 14px; }
333
+ .scene-header {
334
+ display: flex; align-items: baseline; justify-content: space-between; gap: 14px;
335
+ padding-bottom: 12px;
336
+ border-bottom: 1px solid var(--hair);
337
+ }
334
338
  .identity-block { min-width: 0; }
335
- .name { font-size: clamp(28px, 5vw, 40px); letter-spacing: -1.2px; }
339
+ .name { margin: 0; font-size: clamp(26px, 3.6vw, 36px); letter-spacing: -1.2px; line-height: 1.08; font-weight: 600; }
336
340
  .household-line { margin: 2px 0 0; max-width: 44ch; font-size: 13px; }
337
- .header-side { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
341
+ .header-side { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; flex-wrap: wrap; justify-content: flex-end; }
338
342
  .menu-button { padding: 6px 12px; font-size: 13px; }
339
343
  .realtime-pill {
340
344
  display: inline-flex; align-items: center; padding: 5px 10px; border-radius: 999px;
@@ -346,44 +350,54 @@ button:disabled { opacity: 0.4; cursor: default; transform: none; }
346
350
 
347
351
  .scene-stage {
348
352
  display: grid;
349
- grid-template-columns: minmax(0, 520px);
350
- gap: 16px;
353
+ grid-template-columns: minmax(0, 620px) minmax(280px, 330px);
354
+ gap: 24px 36px;
351
355
  align-items: start;
352
356
  justify-content: center;
353
357
  }
354
- .scene-stage.has-sidecar {
355
- grid-template-columns: minmax(0, 500px) minmax(220px, 300px);
356
- }
357
358
  .room-col { width: 100%; min-width: 0; justify-self: center; }
358
359
 
359
- @media (min-width: 1200px) {
360
- .scene-stage.has-sidecar {
361
- position: relative;
362
- grid-template-columns: minmax(0, 520px);
363
- }
364
- .scene-stage.has-sidecar .scene-sidecar {
365
- position: absolute;
366
- top: 0;
367
- left: calc(50% + 276px);
368
- width: 300px;
369
- }
370
- }
371
-
372
360
  /* ---------- the wool room ---------- */
373
361
  #wool-scene {
374
362
  position: relative;
375
363
  border-radius: 26px;
376
364
  overflow: hidden;
377
- border: 1px solid var(--line);
378
- box-shadow: var(--shadow);
365
+ border: 2px solid rgba(110, 90, 72, 0.5);
366
+ box-shadow:
367
+ 0 2px 6px rgba(74, 60, 44, 0.10),
368
+ 0 26px 64px -14px rgba(74, 60, 44, 0.32),
369
+ 0 10px 26px rgba(74, 60, 44, 0.14);
379
370
  -webkit-user-select: none;
380
371
  user-select: none;
381
372
  touch-action: manipulation;
382
373
  }
374
+ #wool-scene::before {
375
+ content: "";
376
+ position: absolute;
377
+ inset: 7px;
378
+ border: 1.5px dashed rgba(244, 236, 220, 0.38);
379
+ border-radius: 19px;
380
+ pointer-events: none;
381
+ z-index: 4;
382
+ }
383
+ #wool-scene::after {
384
+ content: "";
385
+ position: absolute;
386
+ inset: 0;
387
+ border-radius: inherit;
388
+ box-shadow:
389
+ inset 0 1px 0 rgba(244, 236, 220, 0.5),
390
+ inset 0 -16px 24px -14px rgba(46, 36, 26, 0.38);
391
+ pointer-events: none;
392
+ z-index: 4;
393
+ }
394
+ #wool-scene[data-time="dusk"]::before, #wool-scene[data-time="night"]::before {
395
+ border-color: rgba(244, 236, 220, 0.22);
396
+ }
383
397
  #wool-scene > svg { display: block; width: 100%; height: auto; }
384
398
  #wool-scene .hotspot { cursor: pointer; }
385
399
  #dogzone { cursor: pointer; touch-action: none; outline: none; }
386
- #wool-scene [aria-disabled="true"] { cursor: default; }
400
+ #wool-scene [aria-disabled="true"] { cursor: default; pointer-events: none; }
387
401
  #dogzone:focus-visible { outline: 2px dashed rgba(244, 236, 220, 0.7); outline-offset: 6px; }
388
402
  .hotspot:focus-visible,
389
403
  .lamp:focus-visible,
@@ -401,6 +415,7 @@ button:disabled { opacity: 0.4; cursor: default; transform: none; }
401
415
  .moonbtn { opacity: 0; visibility: hidden; pointer-events: none; transition-delay: 0s, 2.4s; }
402
416
  #wool-scene[data-time="night"] .sunbtn { opacity: 0; visibility: hidden; pointer-events: none; transition-delay: 0s, 2.4s; }
403
417
  #wool-scene[data-time="night"] .moonbtn { opacity: 1; visibility: visible; pointer-events: auto; transition-delay: 0s; }
418
+ #wool-scene[data-time="night"] [aria-disabled="true"] { pointer-events: none; }
404
419
  .night-partner-pool { opacity: .05; transition: opacity 1.2s ease; }
405
420
  .night-partner-pool[data-state="afterglow"] { opacity: .18; }
406
421
  .night-partner-pool[data-state="lit"] { opacity: .42; }
@@ -408,6 +423,14 @@ button:disabled { opacity: 0.4; cursor: default; transform: none; }
408
423
  .shelfg,
409
424
  .bowlg,
410
425
  .ballg { filter: url(#object-depth); }
426
+ .guest-lantern,
427
+ .guest-postcard { filter: url(#object-depth); }
428
+ .guest-lantern { opacity: .48; }
429
+ .guest-lamp-pool { opacity: .48; }
430
+ .guest-lamp-pool-far,
431
+ .guest-lantern-far { opacity: .48; }
432
+ .guest-postcard { opacity: .86; }
433
+ .guest-warm-trace { filter: blur(5px); }
411
434
  .lamp > rect,
412
435
  .lamp > path,
413
436
  .lamp > text { filter: url(#object-depth); }
@@ -417,29 +440,29 @@ button:disabled { opacity: 0.4; cursor: default; transform: none; }
417
440
  --dog-body / --dog-belly / --dog-point recolor the SAME shapes; the vars
418
441
  live on the scene (or the adopt preview / a swatch) via data-coat. (The
419
442
  var names are historical — the rig contract predates the species swap.) */
420
- #wool-scene, .adopt-mini-scene, .coat-swatch {
443
+ #wool-scene, .landing-room, .adopt-mini-scene, .coat-swatch {
421
444
  --dog-body: #d98f52; /* marmalade — the default the art is drawn in */
422
445
  --dog-belly: #f6ecda;
423
446
  --dog-point: #c07a40;
424
447
  }
425
- #wool-scene[data-coat="tuxedo"], .adopt-mini-scene[data-coat="tuxedo"], .coat-swatch[data-coat="tuxedo"] {
448
+ #wool-scene[data-coat="tuxedo"], .landing-room[data-coat="tuxedo"], .adopt-mini-scene[data-coat="tuxedo"], .coat-swatch[data-coat="tuxedo"] {
426
449
  --dog-body: #3f3833;
427
450
  --dog-belly: #f4ead6;
428
451
  --dog-point: #2e2824;
429
452
  }
430
- #wool-scene[data-coat="marmalade"], .adopt-mini-scene[data-coat="marmalade"], .coat-swatch[data-coat="marmalade"] {
453
+ #wool-scene[data-coat="marmalade"], .landing-room[data-coat="marmalade"], .adopt-mini-scene[data-coat="marmalade"], .coat-swatch[data-coat="marmalade"] {
431
454
  --dog-body: #d98f52;
432
455
  --dog-belly: #f6ecda;
433
456
  --dog-point: #c07a40;
434
457
  }
435
- #wool-scene[data-coat="ash"], .adopt-mini-scene[data-coat="ash"], .coat-swatch[data-coat="ash"] {
458
+ #wool-scene[data-coat="ash"], .landing-room[data-coat="ash"], .adopt-mini-scene[data-coat="ash"], .coat-swatch[data-coat="ash"] {
436
459
  --dog-body: #8f8a82;
437
460
  --dog-belly: #e6e0d2;
438
461
  --dog-point: #6e675e;
439
462
  }
440
- #wool-scene .coat, .adopt-mini-scene .coat { fill: var(--dog-body); }
441
- #wool-scene .cream, .adopt-mini-scene .cream { fill: var(--dog-belly); }
442
- #wool-scene .point, .adopt-mini-scene .point { fill: var(--dog-point); }
463
+ #wool-scene .coat, .landing-room .coat, .adopt-mini-scene .coat { fill: var(--dog-body); }
464
+ #wool-scene .cream, .landing-room .cream, .adopt-mini-scene .cream { fill: var(--dog-belly); }
465
+ #wool-scene .point, .landing-room .point, .adopt-mini-scene .point { fill: var(--dog-point); }
443
466
 
444
467
  /* coat choice: quiet swatches, the pick shown as the coat itself */
445
468
  .coat-row { display: flex; gap: 8px; align-items: center; margin: 10px 0 4px; }
@@ -620,6 +643,17 @@ button:disabled { opacity: 0.4; cursor: default; transform: none; }
620
643
  #dogmove { transition: opacity 0.5s ease; }
621
644
  #wool-scene.out-walking #dogmove { opacity: 0; }
622
645
 
646
+ #wool-scene.guest-notice #dogmove {
647
+ animation: guest-notice 0.8s ease;
648
+ transform-origin: 200px 470px;
649
+ }
650
+ @keyframes guest-notice {
651
+ 0% { transform: none; }
652
+ 30% { transform: rotate(1.6deg) translateY(-3px); }
653
+ 62% { transform: rotate(-1deg); }
654
+ 100% { transform: none; }
655
+ }
656
+
623
657
  /* brushed nap: grooming lines a stroke leaves in the coat, ~1h */
624
658
  .brushstreak path {
625
659
  stroke: #f2cb97; stroke-width: 4.5; stroke-linecap: round; fill: none;
@@ -755,14 +789,12 @@ button:disabled { opacity: 0.4; cursor: default; transform: none; }
755
789
  color: #f0e4cc; text-shadow: 0 1px 8px rgba(20, 16, 12, 0.6);
756
790
  }
757
791
  .utter.show { opacity: 0.95; transform: translateY(0); }
758
- /* ambient narration: a caption under the framed room, never inside it */
759
792
  .room-caption {
760
- margin: 8px 4px 0;
761
- text-align: center;
762
- font-size: 12px;
793
+ margin: 4px 0 0;
794
+ font-size: 13px;
763
795
  font-style: italic;
764
796
  color: var(--accent);
765
- line-height: 1.35;
797
+ line-height: 1.4;
766
798
  }
767
799
  .whisper {
768
800
  position: absolute;
@@ -774,16 +806,21 @@ button:disabled { opacity: 0.4; cursor: default; transform: none; }
774
806
  border: none;
775
807
  border-top: 1px solid rgba(128, 118, 100, 0.28);
776
808
  border-radius: 0;
777
- background: none;
809
+ background: rgba(239, 232, 216, 0.6);
810
+ backdrop-filter: blur(5px);
811
+ -webkit-backdrop-filter: blur(5px);
778
812
  width: 100%;
779
813
  font-family: var(--serif);
780
814
  font-style: italic;
781
815
  font-size: 13px;
782
816
  color: #5f5342;
783
817
  cursor: text;
818
+ z-index: 5;
784
819
  }
785
820
  .whisper:hover { transform: none; border-color: rgba(128, 118, 100, 0.28); color: #453b2d; }
786
- #wool-scene[data-time="dusk"] .whisper, #wool-scene[data-time="night"] .whisper { color: #c9bda2; }
821
+ #wool-scene[data-time="dusk"] .whisper, #wool-scene[data-time="night"] .whisper {
822
+ color: #c9bda2; background: rgba(38, 32, 24, 0.55);
823
+ }
787
824
  .composer {
788
825
  position: absolute;
789
826
  left: 8px; right: 8px; bottom: 6px;
@@ -796,8 +833,112 @@ button:disabled { opacity: 0.4; cursor: default; transform: none; }
796
833
  .send-quiet { border-color: rgba(154, 138, 114, 0.4); background: rgba(247, 241, 227, 0.9); }
797
834
  .composer-close { padding: 8px 12px; background: rgba(247, 241, 227, 0.9); }
798
835
 
836
+ .glass-tap-wrap {
837
+ position: absolute;
838
+ left: 0; right: 0; bottom: 0;
839
+ z-index: 6;
840
+ display: flex;
841
+ flex-direction: column;
842
+ align-items: center;
843
+ gap: 3px;
844
+ padding: 10px 16px 12px;
845
+ }
846
+ .glass-tap {
847
+ min-height: 44px;
848
+ padding: 0 22px;
849
+ border-radius: 999px;
850
+ border: 1.5px dashed rgba(90, 70, 54, 0.5);
851
+ background: rgba(247, 241, 227, 0.82);
852
+ backdrop-filter: blur(5px);
853
+ -webkit-backdrop-filter: blur(5px);
854
+ color: #55493a;
855
+ font-family: var(--serif);
856
+ font-style: italic;
857
+ font-size: 14px;
858
+ transition: transform 90ms ease-out, border-color 140ms ease, background 140ms ease;
859
+ touch-action: manipulation;
860
+ }
861
+ .glass-tap:hover { transform: none; border-color: rgba(90, 70, 54, 0.7); }
862
+ .glass-tap:active { transform: scale(0.95); }
863
+ .glass-tap:focus-visible { outline: 2px dashed rgba(90, 70, 54, 0.8); outline-offset: 3px; }
864
+ .glass-tap-note {
865
+ margin: 0;
866
+ padding: 3px 7px;
867
+ border-radius: 7px;
868
+ background: rgba(247, 241, 227, 0.92);
869
+ color: #4a3c2c;
870
+ text-shadow: 0 1px 6px rgba(222, 210, 189, 0.85);
871
+ }
872
+ #wool-scene[data-time="dusk"] .glass-tap, #wool-scene[data-time="night"] .glass-tap {
873
+ background: rgba(38, 32, 24, 0.66);
874
+ border-color: rgba(232, 220, 196, 0.4);
875
+ color: #e0d3b6;
876
+ }
877
+ #wool-scene[data-time="dusk"] .glass-tap-note, #wool-scene[data-time="night"] .glass-tap-note {
878
+ color: #b8ab90;
879
+ background: rgba(38, 32, 24, 0.88);
880
+ text-shadow: 0 1px 6px rgba(20, 16, 12, 0.7);
881
+ }
882
+
883
+ .scene-rail {
884
+ display: flex;
885
+ flex-direction: column;
886
+ gap: 18px;
887
+ min-width: 0;
888
+ }
889
+ .rail-story { border-left: 2px solid var(--hair); padding-left: 16px; }
890
+ .rail-kicker { margin: 0; letter-spacing: 1.6px; text-transform: uppercase; }
891
+ .rail-title {
892
+ margin: 6px 0 10px;
893
+ font-size: clamp(19px, 2vw, 23px);
894
+ line-height: 1.2;
895
+ letter-spacing: -0.4px;
896
+ font-weight: 600;
897
+ }
898
+ .rail-copy { margin: 0 0 10px; font-size: 13.5px; line-height: 1.5; }
899
+ .rail-copy:last-child { margin-bottom: 0; }
900
+ .rail-narration { border-left: 2px solid rgba(226, 167, 111, 0.5); padding-left: 16px; }
901
+ .guest-public-story {
902
+ margin: 18px 0;
903
+ padding: 14px 0;
904
+ border-top: 1px solid var(--hair);
905
+ border-bottom: 1px solid var(--hair);
906
+ }
907
+ .guest-public-story-line {
908
+ margin: 5px 0 0;
909
+ color: var(--accent);
910
+ font-size: 17px;
911
+ font-style: italic;
912
+ line-height: 1.4;
913
+ }
914
+
915
+ .room-controls { margin-top: 10px; text-align: center; }
916
+ .room-controls-summary {
917
+ display: inline-flex;
918
+ align-items: center;
919
+ gap: 8px;
920
+ min-height: 44px;
921
+ padding: 6px 16px;
922
+ border: 1px solid var(--hair);
923
+ border-radius: 999px;
924
+ color: var(--muted);
925
+ font-family: var(--sans);
926
+ font-size: 12px;
927
+ letter-spacing: 0.08em;
928
+ cursor: pointer;
929
+ list-style: none;
930
+ transition: color 140ms ease, border-color 140ms ease;
931
+ }
932
+ .room-controls-summary::-webkit-details-marker { display: none; }
933
+ .room-controls-summary::after { content: "▾"; font-size: 10px; transition: transform 180ms ease; }
934
+ .room-controls[open] .room-controls-summary::after { transform: rotate(180deg); }
935
+ .room-controls-summary:hover { color: var(--ink); border-color: var(--line); }
936
+ .room-controls-summary:focus-visible { outline: 2px dashed var(--walnut); outline-offset: 3px; }
937
+ .room-controls .room-verbs { margin-top: 12px; }
938
+ .room-controls .verb { min-height: 44px; padding: 6px 14px; }
939
+
799
940
  /* ---------- exception lane ---------- */
800
- .scene-sidecar { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
941
+ .scene-sidecar { order: -1; display: flex; flex-direction: column; gap: 12px; min-width: 0; }
801
942
  .invite-card { padding: 16px 18px; display: flex; flex-direction: column; gap: 10px; }
802
943
  .invite-title { margin: 0; font-size: 17px; line-height: 1.3; }
803
944
  .invite-link {
@@ -1293,14 +1434,16 @@ button:disabled { opacity: 0.4; cursor: default; transform: none; }
1293
1434
  }
1294
1435
 
1295
1436
  /* ---------- responsive ---------- */
1437
+ @media (max-width: 1080px) {
1438
+ .scene-stage { grid-template-columns: minmax(0, 620px); gap: 18px; }
1439
+ .room-col,
1440
+ .scene-rail { width: 100%; max-width: 620px; justify-self: center; }
1441
+ }
1442
+
1296
1443
  @media (max-width: 860px) {
1297
1444
  .landing-shell { min-height: auto; grid-template-columns: 1fr; align-items: stretch; }
1298
1445
  .adopt-layout { grid-template-columns: 1fr; }
1299
1446
  .adopt-preview { position: static; }
1300
- .scene-stage,
1301
- .scene-stage.has-sidecar { grid-template-columns: minmax(0, 1fr); }
1302
- .room-col,
1303
- .scene-sidecar { width: 100%; max-width: 500px; justify-self: center; }
1304
1447
  }
1305
1448
 
1306
1449
  @media (min-width: 861px) and (max-height: 650px) {
@@ -1322,15 +1465,35 @@ button:disabled { opacity: 0.4; cursor: default; transform: none; }
1322
1465
  .quirk-grid li { scroll-snap-align: center; }
1323
1466
  .quirk-card { height: 100%; }
1324
1467
 
1325
- .scene-header { gap: 8px; }
1468
+ .scene-header { flex-direction: column; align-items: stretch; gap: 8px; }
1469
+ .header-side { width: 100%; justify-content: flex-start; }
1470
+ .guest-badge { text-align: left; }
1326
1471
  .name { font-size: 26px; letter-spacing: -1px; }
1327
1472
  .household-line { font-size: 12px; }
1328
- .menu-button { padding: 5px 10px; font-size: 12px; }
1473
+ .menu-button {
1474
+ padding: 5px 12px; font-size: 12px;
1475
+ min-height: 44px; display: inline-flex; align-items: center;
1476
+ }
1329
1477
  .scene-stage { gap: 10px; }
1330
1478
  .composer, .row { flex-wrap: wrap; }
1479
+ .composer .input { font-size: 16px; }
1480
+ .composer .ghost { min-height: 44px; }
1481
+ .room-controls-summary { min-height: 44px; }
1482
+ .verb { min-height: 44px; padding: 6px 14px; }
1331
1483
  .verb-row { justify-content: center; }
1332
1484
  /* keep the milestone card clear of the header on small screens */
1333
- .milestone { top: 76px; }
1485
+ .milestone { top: 116px; }
1486
+ }
1487
+
1488
+ @media (max-width: 400px) {
1489
+ .glass-tap-wrap {
1490
+ left: auto;
1491
+ right: 4px;
1492
+ top: 4px;
1493
+ bottom: auto;
1494
+ width: 190px;
1495
+ }
1496
+ .glass-tap-note { max-width: 170px; text-align: center; }
1334
1497
  }
1335
1498
 
1336
1499
  /* Short-viewport rescue (iPhone SE class): cap the scene so the whisper line
@@ -1340,7 +1503,7 @@ button:disabled { opacity: 0.4; cursor: default; transform: none; }
1340
1503
  #wool-scene { max-width: 320px; margin: 0 auto; }
1341
1504
  }
1342
1505
 
1343
- @media (min-width: 560px) and (max-width: 860px) and (max-height: 500px) and (orientation: landscape) {
1506
+ @media (min-width: 560px) and (max-width: 1080px) and (max-height: 500px) and (orientation: landscape) {
1344
1507
  .root { padding: 10px 20px; }
1345
1508
  .scene { gap: 8px; }
1346
1509
  .scene-header { align-items: flex-start; }
@@ -1348,25 +1511,23 @@ button:disabled { opacity: 0.4; cursor: default; transform: none; }
1348
1511
  .room-col {
1349
1512
  display: grid;
1350
1513
  grid-template-columns: minmax(168px, min(27vw, 200px)) minmax(220px, 300px);
1351
- grid-template-areas:
1352
- "room caption"
1353
- "room verbs";
1514
+ grid-template-areas: "room controls";
1354
1515
  gap: 10px 16px;
1355
1516
  align-items: center;
1356
1517
  max-width: 540px;
1357
1518
  }
1358
1519
  #wool-scene { grid-area: room; max-width: none; margin: 0; }
1359
- .room-caption {
1360
- grid-area: caption;
1361
- align-self: end;
1362
- margin: 0;
1363
- }
1364
- .room-verbs {
1365
- grid-area: verbs;
1366
- align-self: start;
1367
- justify-content: flex-start;
1368
- margin-top: 0;
1520
+ .room-controls { grid-area: controls; margin-top: 0; text-align: left; }
1521
+ .room-controls .room-verbs { justify-content: flex-start; }
1522
+ .scene.guest-scene .room-col { display: block; max-width: 220px; }
1523
+ .scene.guest-scene .glass-tap-wrap {
1524
+ left: auto;
1525
+ right: 4px;
1526
+ top: 4px;
1527
+ bottom: auto;
1528
+ width: 190px;
1369
1529
  }
1530
+ .scene.guest-scene .glass-tap-note { max-width: 170px; text-align: center; }
1370
1531
  }
1371
1532
 
1372
1533
  @media (prefers-reduced-motion: reduce) {
@@ -1403,7 +1564,11 @@ button:disabled { opacity: 0.4; cursor: default; transform: none; }
1403
1564
  .ballg, .kibbleg circle, .rugwarm, .leashg, #dogmove,
1404
1565
  .brushstreak, .ruglump, .phone-trace, .heartg, .utter,
1405
1566
  .sunbtn, .moonbtn, .mini-dog, .mini-shadow, .mini-eye,
1406
- .mini-mote, .mini-stash { transition: none !important; }
1567
+ .mini-mote, .mini-stash,
1568
+ .glass-tap, .room-controls-summary, .room-controls-summary::after,
1569
+ .access-pet { transition: none !important; animation: none !important; }
1570
+ .glass-tap:active { transform: none; }
1571
+ .room-controls[open] .room-controls-summary::after { transform: rotate(180deg); }
1407
1572
  button,
1408
1573
  [x-transition] {
1409
1574
  animation: none !important;
@@ -1474,6 +1639,7 @@ button:disabled { opacity: 0.4; cursor: default; transform: none; }
1474
1639
  .doorz, .doorpulse .doorglow { animation: none !important; }
1475
1640
  .doorpulse .doorglow { opacity: .5; }
1476
1641
  #dogmove, #visitormove, .doorglow { transition: none !important; }
1642
+ #wool-scene.guest-notice #dogmove { animation: none !important; }
1477
1643
  }
1478
1644
 
1479
1645
  /* detail line-work follows the point wool. (Kept as a generic hook: pack
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: woolpack
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: Standalone validation and authoring tools for Woolroom content packs.
5
5
  License-Expression: MIT AND CC0-1.0
6
6
  Project-URL: Homepage, https://www.minglongpan.com/projects/woolroom
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes