@sobree/core 0.1.6 → 0.1.7

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.
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The narrow surface gesture controllers drive the Viewport through.
3
+ * Controllers translate raw input events into these calls; the Viewport
4
+ * owns all transform state, clamping, and raster behaviour.
5
+ */
6
+ export interface GestureHost {
7
+ /** Pan the stage by `(dx, dy)` CSS pixels. */
8
+ panBy(dx: number, dy: number): void;
9
+ /** Zoom to `scale`, anchoring the point at (clientX, clientY). */
10
+ zoomTo(scale: number, clientX: number, clientY: number): void;
11
+ /** Current visual scale (zoom deltas are multiplicative on this). */
12
+ getScale(): number;
13
+ }
@@ -0,0 +1,38 @@
1
+ import { GestureHost } from './gestureHost';
2
+ /**
3
+ * Touch gestures (mobile): one-finger drag pans, two-finger pinch zooms
4
+ * anchored at the finger midpoint, and translating both fingers pans.
5
+ * Mouse/pen pointers are deliberately ignored — their drag is text
6
+ * selection, not pan. The container has `touch-action: none`, so without
7
+ * this controller touch devices could neither scroll nor zoom.
8
+ */
9
+ export declare class TouchGestures {
10
+ private readonly container;
11
+ private readonly host;
12
+ /** Live touch pointers (pointerId → last client position). */
13
+ private readonly points;
14
+ /** `idle` → no touches; `tap` → one finger down, within slop; `pan` →
15
+ * one finger past slop; `pinch` → two fingers. */
16
+ private mode;
17
+ /** First touch's start position — slop is measured from here. */
18
+ private startX;
19
+ private startY;
20
+ /** Finger distance and scale captured when a pinch begins. */
21
+ private pinchStartDist;
22
+ private pinchStartScale;
23
+ /** Set when a pan/pinch actually moved the stage — the synthetic click
24
+ * browsers fire after the touch sequence must not reach the editor
25
+ * (it would teleport the caret to wherever the drag ended). */
26
+ private suppressNextClick;
27
+ private readonly onPointerDown;
28
+ private readonly onPointerMove;
29
+ private readonly onPointerEnd;
30
+ private readonly onClickCapture;
31
+ constructor(container: HTMLElement, host: GestureHost);
32
+ destroy(): void;
33
+ private handleDown;
34
+ private handleMove;
35
+ private movePinch;
36
+ private handleEnd;
37
+ private beginPinch;
38
+ }
@@ -1,3 +1,4 @@
1
+ import { GestureHost } from './gestureHost';
1
2
  export interface ViewportOptions {
2
3
  minScale?: number;
3
4
  maxScale?: number;
@@ -31,19 +32,18 @@ export interface ViewportOptions {
31
32
  * └ stage (absolutely positioned, transform: translate(tx,ty) scale(s))
32
33
  * └ slot (where the embedded content lives — caller mounts here)
33
34
  *
34
- * Gestures:
35
- * - Zoom: wheel with shiftKey OR ctrlKey (macOS pinch emits ctrlKey).
36
- * The point under the cursor stays under the cursor (zoom-to-cursor).
37
- * - Pan: wheel without modifiers two-finger trackpad scroll deltas move
38
- * the stage. Also supports click-drag with middle mouse or space.
39
- * - Touch (mobile): one-finger drag pans (after a small slop so taps
40
- * still place the caret); two-finger pinch zooms anchored at the
41
- * finger midpoint, and moving both fingers pans. Mouse/pen
42
- * pointers are deliberately excludedmouse drag is text
43
- * selection. The container has `touch-action: none`, so without
44
- * these handlers touch devices could neither scroll nor zoom.
35
+ * Input handling lives in two controllers driving this class through the
36
+ * GestureHost interface:
37
+ * - WheelGestures: zoom on shift/ctrl/meta+wheel (macOS pinch emits
38
+ * ctrlKey), pan on bare wheel with axis-locking.
39
+ * - TouchGestures (mobile): one-finger drag pans (after a small slop so
40
+ * taps still place the caret); two-finger pinch zooms anchored at the
41
+ * finger midpoint. Mouse/pen drag stays text selection.
42
+ *
43
+ * Viewport itself implements GestureHost panBy / zoomTo / getScale are
44
+ * its public API, so the controllers just receive `this`.
45
45
  */
46
- export declare class Viewport {
46
+ export declare class Viewport implements GestureHost {
47
47
  readonly container: HTMLElement;
48
48
  readonly slot: HTMLElement;
49
49
  private readonly stage;
@@ -52,56 +52,19 @@ export declare class Viewport {
52
52
  private ty;
53
53
  private readonly minScale;
54
54
  private readonly maxScale;
55
- private readonly wheelZoomSensitivity;
56
- private readonly pinchZoomSensitivity;
57
55
  private readonly onScaleChange;
58
56
  private readonly onRenderTierChange;
59
57
  private readonly onTransformChange;
60
- private readonly onWheel;
58
+ private readonly wheelGestures;
59
+ private readonly touchGestures;
61
60
  /** Current layout-side zoom factor (integer ≥ 1). See ViewportOptions. */
62
61
  private renderTier;
63
62
  /** Suppresses `onTransformChange` during the constructor's initial
64
63
  * `applyTransform` so consumers can capture `viewport` in their
65
64
  * callback without TDZ traps. Flipped true at the end of the ctor. */
66
65
  private constructed;
67
- /** Timestamp of the last wheel event, used to delimit gestures. */
68
- private gestureLastTime;
69
- /** Dominant axis for the current gesture. Null until detected, cleared at gesture end. */
70
- private gesturePrimaryAxis;
71
- /**
72
- * Signed cumulative dx within the current gesture. Drives lock release:
73
- * wobble (±3-5px back-and-forth) cancels out; sustained one-way motion
74
- * accumulates past the threshold quickly. Reset at gesture end.
75
- */
76
- private gestureSignedDx;
77
- /**
78
- * Sticky horizontal-lock flag. Engaged by `fitTo` so alignment survives
79
- * gentle diagonal trackpad gestures; broken when the gesture's signed
80
- * cumulative dx crosses `X_RELEASE_THRESHOLD` — the user clearly intends
81
- * sustained horizontal motion.
82
- */
83
- private horizontalLock;
84
- /** Live touch pointers (pointerId → last client position). Mouse and
85
- * pen never enter this map — their drag is text selection, not pan. */
86
- private readonly touchPoints;
87
- /** `idle` → no touches; `tap` → one finger down, within slop (a tap
88
- * must still reach the editor to place the caret); `pan` → one finger
89
- * past slop; `pinch` → two fingers. */
90
- private touchMode;
91
- /** First touch's start position — slop is measured from here. */
92
- private touchStartX;
93
- private touchStartY;
94
- /** Finger distance and scale captured when a pinch begins. */
95
- private pinchStartDist;
96
- private pinchStartScale;
97
- private readonly onPointerDown;
98
- private readonly onPointerMove;
99
- private readonly onPointerEnd;
100
- private readonly onClickCapture;
101
- /** Set when a pan/pinch actually moved the stage — the synthetic click
102
- * browsers fire after the touch sequence must not reach the editor
103
- * (it would teleport the caret to wherever the drag ended). */
104
- private suppressNextClick;
66
+ /** Debounce handle for the crisp-text settle pass (see scheduleSettle). */
67
+ private settleTimer;
105
68
  constructor(container: HTMLElement, options?: ViewportOptions);
106
69
  /** Reset pan to origin and scale to 1. */
107
70
  reset(): void;
@@ -129,24 +92,32 @@ export declare class Viewport {
129
92
  /** Zoom to `nextScale`, anchoring the point at (clientX, clientY) in container space. */
130
93
  zoomTo(nextScale: number, clientX: number, clientY: number): void;
131
94
  destroy(): void;
132
- private handleWheel;
133
- private handleTouchDown;
134
- private handleTouchMove;
135
- private handleTouchEnd;
136
- private beginPinch;
95
+ private applyTransform;
137
96
  /**
138
- * Axis-lock for pan gestures:
139
- * - Within a gesture (events GESTURE_GAP_MS apart), a clear dominant
140
- * axis zeros the other axis so a nearly-vertical swipe doesn't also
141
- * drift the paper sideways.
142
- * - An explicit `horizontalLock` set by `fitTo` survives across gestures,
143
- * keeping fit-page / fit-width alignment stable.
144
- * - Both locks release when the gesture's signed cumulative dx crosses
145
- * `X_RELEASE_THRESHOLD`. Signed sum cancels wobble (back-and-forth
146
- * averages to zero) while sustained one-way motion accumulates fast.
97
+ * The stage transform, expressed in the render tier's coordinate space.
98
+ * Chrome applies transforms post-`zoom`, so a `translate(tx, ty)` on an
99
+ * element with `zoom: k` moves by `(tx*k, ty*k)` screen px. We store
100
+ * tx/ty in pre-zoom (container) pixels — the scheme cursor-anchored
101
+ * zoomTo relies on so divide by the tier to cancel zoom's translate
102
+ * multiplication. (Tier is permanently 1; kept for the single code path.)
103
+ *
104
+ * `threeD` selects the gesture-time form (`translate3d` — forces a
105
+ * compositor layer) vs the settled form (plain `translate` — lets the
106
+ * compositor drop the layer pin and re-rasterise text at the effective
107
+ * scale). Same matrix either way; only raster-cache behaviour differs.
147
108
  */
148
- private applyScrollAxisLock;
149
- private applyTransform;
109
+ private transformCss;
110
+ /**
111
+ * Crisp-text pass: once no transform has been written for SETTLE_MS,
112
+ * drop `will-change` and the 3D form so the compositor re-rasterises
113
+ * the (now static) stage at `devicePixelRatio × scale` — text is then
114
+ * as sharp at 3× as a 3×-laid-out page, with zero layout involvement.
115
+ * Both pins are needed: either `will-change: transform` or a 3D
116
+ * transform alone keeps browsers (Safari especially) stretching the
117
+ * stale 1× texture. The next gesture frame re-pins before moving.
118
+ */
119
+ private scheduleSettle;
120
+ private settle;
150
121
  /**
151
122
  * Apply the current transform with a CSS transition. Used only for
152
123
  * programmatic fits — wheel pan/zoom must stay instant or feel sluggish.
@@ -0,0 +1,54 @@
1
+ import { GestureHost } from './gestureHost';
2
+ export interface WheelGestureOptions {
3
+ /** Scale change per unit of wheel deltaY for shift+wheel. */
4
+ wheelZoomSensitivity: number;
5
+ /** Scale change per unit of wheel deltaY for pinch (ctrlKey). */
6
+ pinchZoomSensitivity: number;
7
+ }
8
+ /**
9
+ * Wheel gestures: zoom on shift/ctrl/meta+wheel (macOS trackpad pinch
10
+ * synthesises ctrlKey wheel events), pan on bare wheel — with an
11
+ * axis-lock so a nearly-vertical swipe doesn't drift the paper sideways.
12
+ */
13
+ export declare class WheelGestures {
14
+ private readonly container;
15
+ private readonly host;
16
+ private readonly opts;
17
+ /** Timestamp of the last wheel event, used to delimit gestures. */
18
+ private gestureLastTime;
19
+ /** Dominant axis for the current gesture. Null until detected, cleared at gesture end. */
20
+ private gesturePrimaryAxis;
21
+ /**
22
+ * Signed cumulative dx within the current gesture. Drives lock release:
23
+ * wobble (±3-5px back-and-forth) cancels out; sustained one-way motion
24
+ * accumulates past the threshold quickly. Reset at gesture end.
25
+ */
26
+ private gestureSignedDx;
27
+ /**
28
+ * Sticky horizontal-lock flag. Engaged by `fitTo` so alignment survives
29
+ * gentle diagonal trackpad gestures; broken when the gesture's signed
30
+ * cumulative dx crosses `X_RELEASE_THRESHOLD` — the user clearly
31
+ * intends sustained horizontal motion.
32
+ */
33
+ private horizontalLock;
34
+ private readonly onWheel;
35
+ constructor(container: HTMLElement, host: GestureHost, opts: WheelGestureOptions);
36
+ destroy(): void;
37
+ /** Engage the sticky horizontal lock (fit-* picked an X alignment). */
38
+ engageHorizontalLock(): void;
39
+ /** Clear all gesture and lock state (viewport reset). */
40
+ resetLocks(): void;
41
+ private handleWheel;
42
+ /**
43
+ * Axis-lock for pan gestures:
44
+ * - Within a gesture (events ≤ GESTURE_GAP_MS apart), a clear dominant
45
+ * axis zeros the other axis so a nearly-vertical swipe doesn't also
46
+ * drift the paper sideways.
47
+ * - An explicit `horizontalLock` set by `fitTo` survives across gestures,
48
+ * keeping fit-page / fit-width alignment stable.
49
+ * - Both locks release when the gesture's signed cumulative dx crosses
50
+ * `X_RELEASE_THRESHOLD`. Signed sum cancels wobble (back-and-forth
51
+ * averages to zero) while sustained one-way motion accumulates fast.
52
+ */
53
+ private applyScrollAxisLock;
54
+ }
package/dist/index.css CHANGED
@@ -1 +1 @@
1
- .paper-stack{display:flex;flex-direction:column;padding:48px;gap:28px;outline:none}.paper-row{display:flex;flex-direction:row;align-items:flex-start;gap:24px}.paper{position:relative;background:#fff;border:1px solid rgba(0,0,0,.3);box-shadow:0 1px 2px #00000014,0 18px 60px #0000001f;padding-top:var(--margin-top, 25mm);padding-right:var(--margin-right, 20mm);padding-bottom:var(--margin-bottom, 25mm);padding-left:var(--margin-left, 20mm);overflow:hidden;flex:none}.paper-header,.paper-footer{position:absolute;left:var(--margin-left, 20mm);right:var(--margin-right, 20mm);font-size:10pt;color:#555;white-space:pre-wrap}.paper-header{top:0;min-height:var(--margin-top, 25mm);padding-top:var(--header-offset-mm, 12.7mm);padding-bottom:4mm;border-bottom:1px dashed transparent}.paper-footer{bottom:0;min-height:var(--margin-bottom, 25mm);padding-top:4mm;text-align:center;border-top:1px dashed transparent}.paper-anchors{position:absolute;top:0;right:0;bottom:0;left:0;pointer-events:none;isolation:isolate;z-index:1}.paper-anchors.is-empty{display:none}.paper-anchors-behind{position:absolute;top:0;right:0;bottom:0;left:0;pointer-events:none;isolation:isolate;z-index:0}.paper-anchors-behind.is-empty{display:none}.paper-zone-anchors{position:absolute;top:0;right:0;bottom:0;left:0;pointer-events:none;isolation:isolate;z-index:2}.paper-zone-anchors.is-empty{display:none}.paper-anchor{position:absolute;box-sizing:border-box;overflow:hidden}.paper-header>p,.paper-footer>p,.paper-header>ul,.paper-header>ol,.paper-footer>ul,.paper-footer>ol{margin:0}.paper-footnotes{position:absolute;left:var(--margin-left, 20mm);right:var(--margin-right, 20mm);bottom:var(--margin-bottom, 25mm);font-size:.85em;padding-top:.5em;border-top:1pt solid currentColor;background:#fffffff5;z-index:1;max-height:40%;overflow:hidden}.paper-footnotes.is-empty{display:none}.paper-comments{flex:0 0 70mm;max-width:70mm;font-size:.85em;padding:0 .5em;align-self:stretch;overflow-y:auto}.paper-comments.is-empty{display:none}.paper-footnotes .sobree-footnotes__list{margin:0;padding-left:1.5em}.paper-footnotes .sobree-footnotes__item{margin:.25em 0}.paper-content a{color:inherit;text-decoration:none}.paper-content{position:relative;height:100%;min-height:100px;display:flow-root}.paper-content td p:not([style*=line-height]),.paper-content td li:not([style*=line-height]),.paper-content th p:not([style*=line-height]),.paper-content th li:not([style*=line-height]){line-height:1}.paper-content td p:not([style*=margin]),.paper-content th p:not([style*=margin]){margin:0}.paper-content .sobree-hang>li{list-style:none}.paper-content .sobree-hang>li:before{display:inline-block;box-sizing:border-box;width:var(--sobree-list-hang, 0);margin-left:calc(-1 * var(--sobree-list-hang, 0));white-space:nowrap;color:var(--sobree-marker-color, currentColor);font-family:var(--sobree-marker-font, inherit);font-size:var(--sobree-marker-size, inherit)}.paper-content .sobree-hang.lst-bullet>li:before{content:var(--sobree-bullet, "•")}.paper-content .sobree-hang.lst-decimal>li:before{content:var(--mk-pre, "") counter(list-item,decimal) var(--mk-suf, ".")}.paper-content .sobree-hang.lst-decimal-zero>li:before{content:var(--mk-pre, "") counter(list-item,decimal-leading-zero) var(--mk-suf, ".")}.paper-content .sobree-hang.lst-lower-latin>li:before{content:var(--mk-pre, "") counter(list-item,lower-latin) var(--mk-suf, ".")}.paper-content .sobree-hang.lst-upper-latin>li:before{content:var(--mk-pre, "") counter(list-item,upper-latin) var(--mk-suf, ".")}.paper-content .sobree-hang.lst-lower-roman>li:before{content:var(--mk-pre, "") counter(list-item,lower-roman) var(--mk-suf, ".")}.paper-content .sobree-hang.lst-upper-roman>li:before{content:var(--mk-pre, "") counter(list-item,upper-roman) var(--mk-suf, ".")}.paper-content .sobree-tab-spread{display:flex;justify-content:space-between;align-items:baseline;gap:1em;white-space:nowrap}.paper-content .sobree-tab-spread .sobree-tab-spread__before,.paper-content .sobree-tab-spread .sobree-tab-spread__after{white-space:pre-wrap;min-width:0}.paper-content table.sobree-table-bordered td,.paper-content table.sobree-table-bordered th{border:var(--table-cell-border, none);padding:0 4px}.sobree-editor{position:relative;outline:none}.sobree-editor>:first-child{margin-top:0}.sobree-editor p,.sobree-editor h1,.sobree-editor h2,.sobree-editor h3,.sobree-editor h4,.sobree-editor h5,.sobree-editor h6,.sobree-editor ul,.sobree-editor ol,.sobree-editor li,.sobree-editor blockquote,.sobree-editor pre{margin:0;padding:0}.sobree-editor ul,.sobree-editor ol{padding-left:1.5em}.sobree-editor li.sobree-li-continuation{list-style-type:none}.sobree-editor li.sobree-li-continuation::marker{content:""}.sobree-editor .sobree-fragment-continued{text-align-last:justify}.sobree-editor .sobree-footnote-ref a{color:inherit;text-decoration:none}.sobree-editor .sobree-footnote-ref a:hover{text-decoration:underline}.sobree-editor .sobree-footnotes{margin-top:2em;padding-top:.5em;border-top:1pt solid currentColor;font-size:.85em}.sobree-editor .sobree-footnotes__list{padding-left:1.5em}.sobree-editor .sobree-footnotes__item{margin:.25em 0}.sobree-editor ins.sobree-revision-ins{text-decoration:underline}.sobree-editor del.sobree-revision-del{text-decoration:line-through}.sobree-editor .sobree-revision-format{text-decoration:underline dashed var(--sobree-format-revision-color, currentColor);text-underline-offset:3px}.sobree-editor [data-block-revision=ins]:after,.sobree-editor [data-block-revision=del]:after{content:" ¶";color:var(--sobree-block-revision-color, currentColor);opacity:.65;font-weight:600;-webkit-user-select:none;user-select:none}.sobree-editor [data-block-revision=del]:after{text-decoration:line-through}.sobree-editor .sobree-comment-range{background:var(--sobree-comment-range-bg, rgba(255, 217, 0, .25));border-bottom:1px dotted var(--sobree-comment-range-border, rgba(180, 130, 0, .5))}.sobree-editor .sobree-comment-ref{display:inline;font-size:.85em;margin:0 .1em}.sobree-editor .sobree-comment-ref a{color:inherit;text-decoration:none}.sobree-editor .sobree-comment-ref a:hover{filter:brightness(.7)}.sobree-editor h1,.sobree-editor h2,.sobree-editor h3,.sobree-editor h4,.sobree-editor h5,.sobree-editor h6{font-size:inherit;font-weight:inherit}.sobree-editor table{border-collapse:collapse;width:100%;position:relative}.sobree-editor th,.sobree-editor td{border:none;padding:0 .08in;vertical-align:top;transition:border-color .12s ease}.sobree-editor .sobree-section-break{display:flex;align-items:center;gap:8px;margin:8px 0;color:var(--fg-subtle, #7c7764);font-size:11px;letter-spacing:.04em;text-transform:uppercase;-webkit-user-select:none;user-select:none}.sobree-editor .sobree-section-break--continuous{visibility:hidden;height:0;margin:0;padding:0;border:none;overflow:hidden;font-size:0;line-height:0}.sobree-editor .sobree-section-break:before,.sobree-editor .sobree-section-break:after{content:"";flex:1;border-top:1px dashed var(--border, #dedbd0)}.sobree-editor .sobree-section-break__label{flex:none;padding:0 6px;background:var(--bg-elevated, #fff)}.sobree-editor .sobree-textbox-frame p{line-height:1.2}.sobree-editor .paper-content p,.sobree-editor .paper-content li,.sobree-editor .paper-header p,.sobree-editor .paper-header li,.sobree-editor .paper-footer p,.sobree-editor .paper-footer li,.sobree-editor .sobree-textbox-frame p{white-space:pre-wrap}.sobree-editor .sobree-section-trailer-empty{height:0;line-height:0;font-size:0;margin:0;padding:0;overflow:hidden}.sobree-editor .sobree-textbox-frame--placeholder{border:1px solid var(--border, #c8c4b8)}.sobree-editor .sobree-textbox-frame--placeholder.sobree-textbox-frame--filled{border:none;z-index:-1}.sobree-editor img.is-selected{outline:2px solid var(--sobree-primary, #d4521f);outline-offset:2px}.sobree-image-resize-handle{position:absolute;width:16px;height:16px;background:var(--sobree-primary, #d4521f);border:2px solid #fff;border-radius:3px;cursor:nwse-resize;z-index:1000;-webkit-user-select:none;user-select:none}.sobree-viewport{position:relative;overflow:hidden;overscroll-behavior:contain;touch-action:none;background:#ececee}.sobree-viewport__stage{position:absolute;top:0;left:0;transform-origin:0 0;will-change:transform}.sobree-viewport__stage.is-animating{transition:transform .32s cubic-bezier(.2,.7,.2,1)}.sobree-viewport__slot{display:block}.paper-stack .paper-content,.paper-stack .paper-header,.paper-stack .paper-footer{transition:opacity .2s ease}.paper-stack.is-zone-editing .paper-content,.paper-stack.is-zone-editing .paper-header,.paper-stack.is-zone-editing .paper-footer{opacity:.2}.paper-stack.is-zone-editing .paper-header[contenteditable=true],.paper-stack.is-zone-editing .paper-footer[contenteditable=true]{opacity:1}.paper-header[contenteditable=true],.paper-footer[contenteditable=true]{outline:2px dotted var(--primary, #c96f22);outline-offset:4px;background:var(--primary-soft, #fdf6ee);color:var(--fg-strong, #14130f);border-radius:2px}
1
+ .paper-stack{display:flex;flex-direction:column;padding:48px;gap:28px;outline:none}.paper-row{display:flex;flex-direction:row;align-items:flex-start;gap:24px}.paper{position:relative;background:#fff;border:1px solid rgba(0,0,0,.3);box-shadow:0 1px 2px #00000014,0 18px 60px #0000001f;padding-top:var(--margin-top, 25mm);padding-right:var(--margin-right, 20mm);padding-bottom:var(--margin-bottom, 25mm);padding-left:var(--margin-left, 20mm);overflow:hidden;flex:none}.paper-header,.paper-footer{position:absolute;left:var(--margin-left, 20mm);right:var(--margin-right, 20mm);font-size:10pt;color:#555;white-space:pre-wrap}.paper-header{top:0;min-height:var(--margin-top, 25mm);padding-top:var(--header-offset-mm, 12.7mm);padding-bottom:4mm;border-bottom:1px dashed transparent}.paper-footer{bottom:0;min-height:var(--margin-bottom, 25mm);padding-top:4mm;text-align:center;border-top:1px dashed transparent}.paper-anchors{position:absolute;top:0;right:0;bottom:0;left:0;pointer-events:none;isolation:isolate;z-index:1}.paper-anchors.is-empty{display:none}.paper-anchors-behind{position:absolute;top:0;right:0;bottom:0;left:0;pointer-events:none;isolation:isolate;z-index:0}.paper-anchors-behind.is-empty{display:none}.paper-zone-anchors{position:absolute;top:0;right:0;bottom:0;left:0;pointer-events:none;isolation:isolate;z-index:2}.paper-zone-anchors.is-empty{display:none}.paper-anchor{position:absolute;box-sizing:border-box;overflow:hidden}.paper-header>p,.paper-footer>p,.paper-header>ul,.paper-header>ol,.paper-footer>ul,.paper-footer>ol{margin:0}.paper-footnotes{position:absolute;left:var(--margin-left, 20mm);right:var(--margin-right, 20mm);bottom:var(--margin-bottom, 25mm);font-size:.85em;padding-top:.5em;border-top:1pt solid currentColor;background:#fffffff5;z-index:1;max-height:40%;overflow:hidden}.paper-footnotes.is-empty{display:none}.paper-comments{flex:0 0 70mm;max-width:70mm;font-size:.85em;padding:0 .5em;align-self:stretch;overflow-y:auto}.paper-comments.is-empty{display:none}.paper-footnotes .sobree-footnotes__list{margin:0;padding-left:1.5em}.paper-footnotes .sobree-footnotes__item{margin:.25em 0}.paper-content a{color:inherit;text-decoration:none}.paper-content{position:relative;height:100%;min-height:100px;display:flow-root}.paper-content td p:not([style*=line-height]),.paper-content td li:not([style*=line-height]),.paper-content th p:not([style*=line-height]),.paper-content th li:not([style*=line-height]){line-height:1}.paper-content td p:not([style*=margin]),.paper-content th p:not([style*=margin]){margin:0}.paper-content .sobree-hang>li{list-style:none}.paper-content .sobree-hang>li:before{display:inline-block;box-sizing:border-box;width:var(--sobree-list-hang, 0);margin-left:calc(-1 * var(--sobree-list-hang, 0));white-space:nowrap;color:var(--sobree-marker-color, currentColor);font-family:var(--sobree-marker-font, inherit);font-size:var(--sobree-marker-size, inherit)}.paper-content .sobree-hang.lst-bullet>li:before{content:var(--sobree-bullet, "•")}.paper-content .sobree-hang.lst-decimal>li:before{content:var(--mk-pre, "") counter(list-item,decimal) var(--mk-suf, ".")}.paper-content .sobree-hang.lst-decimal-zero>li:before{content:var(--mk-pre, "") counter(list-item,decimal-leading-zero) var(--mk-suf, ".")}.paper-content .sobree-hang.lst-lower-latin>li:before{content:var(--mk-pre, "") counter(list-item,lower-latin) var(--mk-suf, ".")}.paper-content .sobree-hang.lst-upper-latin>li:before{content:var(--mk-pre, "") counter(list-item,upper-latin) var(--mk-suf, ".")}.paper-content .sobree-hang.lst-lower-roman>li:before{content:var(--mk-pre, "") counter(list-item,lower-roman) var(--mk-suf, ".")}.paper-content .sobree-hang.lst-upper-roman>li:before{content:var(--mk-pre, "") counter(list-item,upper-roman) var(--mk-suf, ".")}.paper-content .sobree-tab-spread{display:flex;justify-content:space-between;align-items:baseline;gap:1em;white-space:nowrap}.paper-content .sobree-tab-spread .sobree-tab-spread__before,.paper-content .sobree-tab-spread .sobree-tab-spread__after{white-space:pre-wrap;min-width:0}.paper-content table.sobree-table-bordered td,.paper-content table.sobree-table-bordered th{border:var(--table-cell-border, none);padding:0 4px}.sobree-editor{position:relative;outline:none}.sobree-editor>:first-child{margin-top:0}.sobree-editor p,.sobree-editor h1,.sobree-editor h2,.sobree-editor h3,.sobree-editor h4,.sobree-editor h5,.sobree-editor h6,.sobree-editor ul,.sobree-editor ol,.sobree-editor li,.sobree-editor blockquote,.sobree-editor pre{margin:0;padding:0}.sobree-editor ul,.sobree-editor ol{padding-left:1.5em}.sobree-editor li.sobree-li-continuation{list-style-type:none}.sobree-editor li.sobree-li-continuation::marker{content:""}.sobree-editor .sobree-fragment-continued{text-align-last:justify}.sobree-editor .sobree-footnote-ref a{color:inherit;text-decoration:none}.sobree-editor .sobree-footnote-ref a:hover{text-decoration:underline}.sobree-editor .sobree-footnotes{margin-top:2em;padding-top:.5em;border-top:1pt solid currentColor;font-size:.85em}.sobree-editor .sobree-footnotes__list{padding-left:1.5em}.sobree-editor .sobree-footnotes__item{margin:.25em 0}.sobree-editor ins.sobree-revision-ins{text-decoration:underline}.sobree-editor del.sobree-revision-del{text-decoration:line-through}.sobree-editor .sobree-revision-format{text-decoration:underline dashed var(--sobree-format-revision-color, currentColor);text-underline-offset:3px}.sobree-editor [data-block-revision=ins]:after,.sobree-editor [data-block-revision=del]:after{content:" ¶";color:var(--sobree-block-revision-color, currentColor);opacity:.65;font-weight:600;-webkit-user-select:none;user-select:none}.sobree-editor [data-block-revision=del]:after{text-decoration:line-through}.sobree-editor .sobree-comment-range{background:var(--sobree-comment-range-bg, rgba(255, 217, 0, .25));border-bottom:1px dotted var(--sobree-comment-range-border, rgba(180, 130, 0, .5))}.sobree-editor .sobree-comment-ref{display:inline;font-size:.85em;margin:0 .1em}.sobree-editor .sobree-comment-ref a{color:inherit;text-decoration:none}.sobree-editor .sobree-comment-ref a:hover{filter:brightness(.7)}.sobree-editor h1,.sobree-editor h2,.sobree-editor h3,.sobree-editor h4,.sobree-editor h5,.sobree-editor h6{font-size:inherit;font-weight:inherit}.sobree-editor table{border-collapse:collapse;width:100%;position:relative}.sobree-editor th,.sobree-editor td{border:none;padding:0 .08in;vertical-align:top;transition:border-color .12s ease}.sobree-editor .sobree-section-break{display:flex;align-items:center;gap:8px;margin:8px 0;color:var(--fg-subtle, #7c7764);font-size:11px;letter-spacing:.04em;text-transform:uppercase;-webkit-user-select:none;user-select:none}.sobree-editor .sobree-section-break--continuous{visibility:hidden;height:0;margin:0;padding:0;border:none;overflow:hidden;font-size:0;line-height:0}.sobree-editor .sobree-section-break:before,.sobree-editor .sobree-section-break:after{content:"";flex:1;border-top:1px dashed var(--border, #dedbd0)}.sobree-editor .sobree-section-break__label{flex:none;padding:0 6px;background:var(--bg-elevated, #fff)}.sobree-editor .sobree-textbox-frame p{line-height:1.2}.sobree-editor .paper-content p,.sobree-editor .paper-content li,.sobree-editor .paper-header p,.sobree-editor .paper-header li,.sobree-editor .paper-footer p,.sobree-editor .paper-footer li,.sobree-editor .sobree-textbox-frame p{white-space:pre-wrap}.sobree-editor .sobree-section-trailer-empty{height:0;line-height:0;font-size:0;margin:0;padding:0;overflow:hidden}.sobree-editor .sobree-textbox-frame--placeholder{border:1px solid var(--border, #c8c4b8)}.sobree-editor .sobree-textbox-frame--placeholder.sobree-textbox-frame--filled{border:none;z-index:-1}.sobree-editor img.is-selected{outline:2px solid var(--sobree-primary, #d4521f);outline-offset:2px}.sobree-image-resize-handle{position:absolute;width:16px;height:16px;background:var(--sobree-primary, #d4521f);border:2px solid #fff;border-radius:3px;cursor:nwse-resize;z-index:1000;-webkit-user-select:none;user-select:none}.sobree-viewport{position:relative;overflow:hidden;overscroll-behavior:contain;touch-action:none;background:#ececee}.sobree-viewport__stage{position:absolute;top:0;left:0;transform-origin:0 0}.sobree-viewport__stage.is-gesturing,.sobree-viewport__stage.is-animating{will-change:transform}.sobree-viewport__stage.is-animating{transition:transform .32s cubic-bezier(.2,.7,.2,1)}.sobree-viewport__slot{display:block}.paper-stack .paper-content,.paper-stack .paper-header,.paper-stack .paper-footer{transition:opacity .2s ease}.paper-stack.is-zone-editing .paper-content,.paper-stack.is-zone-editing .paper-header,.paper-stack.is-zone-editing .paper-footer{opacity:.2}.paper-stack.is-zone-editing .paper-header[contenteditable=true],.paper-stack.is-zone-editing .paper-footer[contenteditable=true]{opacity:1}.paper-header[contenteditable=true],.paper-footer[contenteditable=true]{outline:2px dotted var(--primary, #c96f22);outline-offset:4px;background:var(--primary-soft, #fdf6ee);color:var(--fg-strong, #14130f);border-radius:2px}